<commit_before>
/**
 * Canvas Class
 *
 * @param {int} width
 * @param {int} height
 * @param {string} bgColor
 *
 */
function Canvas(width, height, bgColor)
{
	this.el = document.createElement('canvas');
	this.ctx = this.el.getContext('2d');

	var size = {
		width : (width) ? width : 150,
		height : (height) ? height : 100
	};

	this.el.width = size.width;
	this.el.height = size.height;

	if (bgColor)
	{
		this.ctx.fillStyle = bgColor;
		this.ctx.fillRect(0, 0, size.width, size.height);
	}
}

/**
 * Make image
 *
 * @param {Object} options
 * @param {Object} options.image         image element
 * @param {int}    options.resampleCount
 * @param {int}    options.width
 * @param {int}    options.height
 * @param {int}    options.cx            crop x
 * @param {int}    options.cy            crop y
 * @param {int}    options.cw            crop width
 * @param {int}    options.ch            crop height
 * @param {int}    options.dx
 * @param {int}    options.dy
 * @param {int}    options.dw
 * @param {int}    options.dh
 * @param {String} options.bgColor
 * @param {Function} callback
 */
function makeImage(options, callback)
{
	// set limit resampling count
	options.resampleCount = (options.resampleCount > 0) ? options.resampleCount : 0;
	options.resampleCount = (options.resampleCount < 4) ? options.resampleCount : 4;

	var resampleMax = Math.pow(2, options.resampleCount);
	var canvas = new Canvas(options.width * resampleMax, options.height * resampleMax, options.bgColor);

	// resize canvas
	var resizeCanvas = function(count, parentCanvas)
	{
		var max = Math.pow(2, count);
		var canvasForResize = new Canvas(options.width * max, options.height * max, options.bgColor);

		canvasForResize.ctx.drawImage(parentCanvas.el, 0, 0, parentCanvas.el.width * 0.5, parentCanvas.el.height * 0.5);

		if (count > 0)
		{
			resizeCanvas(count-1, canvasForResize);
		}
		else
		{
			if (callback)
			{
				callback(canvasForResize);
			}
		}
	};

	// init draw image
	canvas.ctx.drawImage(
		options.image,
		(options.cx), // cx
		(options.cy), // cy
		(options.cw), // cw
		(options.ch), // ch
		(options.dx * resampleMax), // dx
		(options.dy * resampleMax), // dy
		(options.dw * resampleMax), // dw
		(options.dh * resampleMax) // dh
	);

	if (options.resampleCount > 0)
	{
		resizeCanvas(options.resampleCount - 1, canvas);
	}
	else
	{
		if (callback)
		{
			callback(canvas);
		}
	}
}

/**
 * Get image size
 *
 * @param {String} type
 * @param {int} cw container width
 * @param {int} ch container height
 * @param {int} iw image width
 * @param {int} ih image height
 * @return {Object}
 */
function getImageSize(type, cw, ch, iw, ih)
{
	var size  = {
		width : 0,
		height : 0
	};

	switch(type)
	{
		case 'cover':
			if (cw > ch)
			{
				size.width = cw;
				size.height = ih * (cw / iw);
				if (ch > size.height)
				{
					size.width = iw * (ch / ih);
					size.height = ch;
				}
			}
			else
			{
				size.width = iw * (ch / ih);
				size.height = ch;
				if (cw > size.width)
				{
					size.width = cw;
					size.height = ih * (cw / iw);
				}
			}
			break;

		case 'width':
			size.width = cw;
			size.height = ih * (cw / iw);
			break;

		case 'height':
			size.width = iw * (ch / ih);
			size.height = ch;
			break;

		default:
			size.width = cw;
			size.height = ch;
			break;
	}

	return {
		width : Math.round(size.width),
		height : Math.round(size.height)
	};
}

/**
 * Get image size
 *
 * @param {Object} el
 * @param {String} outputType
 * @param {int} quality
 * @return {Object}
 */
function canvasToBase64(el, outputType, quality)
{
	outputType = outputType || 'image/jpeg';
	quality = quality || 0.75;
	return el.toDataURL(outputType, (outputType == 'image/jpeg') ? quality : null);
}

module.exports = {

	container : null,
	gridster : null,

	/**
	 * init
	 *
	 * @param {object} container
	 */
	init(container)
	{
		this.container = container;
		this.gridster = container.refs.gridster;
	},

	/**
	 * Object to json
	 *
	 * @param {Object} src
	 * @param {int} space
	 * @return {String}
	 */
	objectToJson(src, space)
	{
		return JSON.stringify(src, null, (space || 4));
	},

	/**
	 * Export gridster
	 *
	 * @return {Object}
	 */
	exportGridster()
	{
		const $gridster = this.gridster.$gridster;
		const gridster = this.gridster.gridster;
		var blockData = [];

		$gridster.find('li').each((k, o) => {
			const $o = $(o);
			var data = {};

			data.color = $o.attr('data-color');

			if ($o.hasClass('attached'))
			{
				data.position = $o.children('figure').attr('data-position');
				data.size = $o.children('figure').attr('data-size');
				data.image = $o.children('figure').attr('data-image');
			}

			blockData.push(data);
		});

		return {
			params : gridster.serialize(),
			figure : blockData
		};
	},

	/**
	 * Get ratio
	 *
	 * @param {int} w
	 * @param {int} h
	 * @return {int}
	 */
	getRatio(w, h)
	{
		var result = parseInt(w) / parseInt(h);
		result = Math.round(result * 1000) / 1000;
		return result;
	},

	/**
	 * Make queue
	 *
	 * @param {Object} gridsterData
	 * @return {Array}
	 */
	makeQueue(gridsterData)
	{
		var queue = [];
		var pref = this.container.state.preference;
		var getItemSize = (width, col, margin) => {
			return (width * col) + ((col > 1) ? margin * (col - 1) : 0);
		};

		gridsterData.figure.forEach((o, k) => {
			var param = gridsterData.params[k];
			if (o.image)
			{
				queue.push({
					key : k,
					image : o.image,
					position : o.position,
					size : o.size,
					targetWidth : getItemSize(pref.width, param.size_x, pref.inner_margin),
					targetHeight : getItemSize(pref.height, param.size_y, pref.inner_margin),
					color : o.color
				});
			}
		});

		return queue;
	},

	/**
	 * Play queue
	 *
	 * @param {Array} queue
	 * @param {String} outputType
	 * @param {int} quality
	 * @param {Function} callback
	 * @return {Object}
	 */
	playQueue(queue, outputType, quality, callback)
	{
		var max = queue.length;
		var result = [];
		quality = quality || 0.75;

		function draw(key)
		{
			var img = new Image();
			var data = queue[key];

			img.onload = (e) => {
				let realSize = {
					width: img.naturalWidth,
					height: img.naturalHeight
				};
				let size = {};
				let position = {};
				let pos = [];

				if (data.size == 'cover')
				{
					size = getImageSize('cover', data.targetWidth, data.targetHeight, realSize.width, realSize.height);
					position.x = (data.targetWidth * 0.5) - (size.width * 0.5);
					position.y = (data.targetHeight * 0.5) - (size.height * 0.5);
				}
				else
				{
					pos = data.position.split(' ');
					size = getImageSize('width', data.targetWidth, data.targetHeight, realSize.width, realSize.height);
					position.x = parseInt(pos[0]);
					position.y = parseInt(pos[1]);
				}

				makeImage({
					image : img,
					resampleCount : 1,
					width : data.targetWidth,
					height : data.targetHeight,
					cx : 0,
					cy : 0,
					cw : realSize.width,
					ch : realSize.height,
					dx : position.x,
					dy : position.y,
					dw : size.width,
					dh : size.height,
					bgColor : data.color
				}, (canvas) => {
					key += 1;
					result.push({
						key : data.key,
						data : canvasToBase64(canvas.el, outputType, quality)
					});
					if (max > key)
					{
						draw(key);
					}
					else
					{
						if (callback)
						{
							callback(result);
						}
					}

				});
			};
			img.src = data.image;
		}

		if (max > 0)
		{
			draw(0);
		}
		else
		{
			if (callback)
			{
				callback(result);
			}
		}
	},

	/**
	 * Get export data
	 *
	 * @param {Object} resource
	 * @return {Object}
	 */
	getExportData(resource)
	{
		return {
			gridster : resource,
			preference : this.container.state.preference
		};
	},

	/**
	 * Draw canvas
	 *
	 * @param {Object} canvas
	 * @param {Object} gridData
	 * @param {Object} pref
	 * @param {Boolean} isImage
	 * @param {Function} callback
	 */
	drawCanvas(canvas, gridData, pref, isImage, callback)
	{
		gridData.params.forEach((o, k) => {
			var figures = gridData.figure[k];
			var block = {
				x : (pref.width + pref.inner_margin) * (o.col - 1) + (pref.outer_margin + pref.inner_margin),
				y : (pref.height + pref.inner_margin) * (o.row - 1) + (pref.outer_margin + pref.inner_margin),
				width : (o.size_x * pref.width) + ((o.size_x > 1) ? (pref.inner_margin * (o.size_x-1)) : 0),
				height : (o.size_y * pref.height) + ((o.size_y > 1) ? (pref.inner_margin * (o.size_y-1)) : 0)
			};

			if (figures.image)
			{
				var img = new Image();
				img.onload = (e) => {
					canvas.ctx.drawImage(
						img,
						block.x, // dx
						block.y, // dy
						block.width, // dw
						block.height // dh
					);

					if (isImage && callback && ((gridData.params.length - 1) == k))
					{
						callback(canvas);
					}
				};
				img.src = figures.image;
			}
			else
			{
				canvas.ctx.fillStyle = (figures.color) ? figures.color : '#ffffff';
				canvas.ctx.fillRect(block.x, block.y, block.width, block.height);
			}

			if (!isImage && callback && ((gridData.params.length - 1) == k))
			{
				callback(canvas);
			}
		});
	},

	/*** EXPORT METHODS ***/

	/**
	 * Basic
	 *
	 * @return {Object}
	 */
	json()
	{
		return this.objectToJson(
			this.getExportData( this.exportGridster() ),
			null
		);
	},

	/**
	 * Console
	 *
	 * @return {Object}
	 */
	console()
	{
		console.log( this.getExportData( this.exportGridster() ) );
	},

	/**
	 * Packed
	 *
	 * @param {Function} callback
	 */
	packed(callback)
	{
		var result = this.exportGridster();
		var queue = this.makeQueue(result);
		this.playQueue(queue, 'image/jpeg', 0.8, (imgResult) => {
			imgResult.forEach((o) => {
				result.figure[o.key].image = o.data;
				result.figure[o.key].position = '50% 50%';
				result.figure[o.key].size = 'cover';
			});
			if (callback)
			{
				callback( this.objectToJson(this.getExportData(result), null) );
			}
		});
	},

	/**
	 * Image
	 *
	 * @param {Function} callback
	 */
	image(callback)
	{
		var gridData = this.exportGridster();
		var pref = this.container.state.preference;
		var queue = this.makeQueue(gridData);
		var canvas = new Canvas(
			this.gridster.$gridster.width() + (pref.outer_margin * 2) + (pref.inner_margin),
			this.gridster.$gridster.height() + (pref.outer_margin * 2) + (pref.inner_margin),
			'#ffffff');
		//log(gridData);

		this.playQueue(queue, 'image/png', 0, (imgResult) => {
			imgResult.forEach((o) => {
				gridData.figure[o.key].image = o.data;
				gridData.figure[o.key].position = '0 0';
				gridData.figure[o.key].size = 'cover';
			});

			this.drawCanvas(canvas, gridData, pref, (imgResult.length > 0), (canvas) => {
				if (callback)
				{
					callback(canvasToBase64(canvas.el, 'image/jpeg', 0.85));
				}
			});
		});
	}
};
<commit_msg>이미지 그리기 콜백 타이밍 오류수정<commit_after>
/**
 * Canvas Class
 *
 * @param {int} width
 * @param {int} height
 * @param {string} bgColor
 *
 */
function Canvas(width, height, bgColor)
{
	this.el = document.createElement('canvas');
	this.ctx = this.el.getContext('2d');

	var size = {
		width : (width) ? width : 150,
		height : (height) ? height : 100
	};

	this.el.width = size.width;
	this.el.height = size.height;

	if (bgColor)
	{
		this.ctx.fillStyle = bgColor;
		this.ctx.fillRect(0, 0, size.width, size.height);
	}
}

/**
 * Make image
 *
 * @param {Object} options
 * @param {Object} options.image         image element
 * @param {int}    options.resampleCount
 * @param {int}    options.width
 * @param {int}    options.height
 * @param {int}    options.cx            crop x
 * @param {int}    options.cy            crop y
 * @param {int}    options.cw            crop width
 * @param {int}    options.ch            crop height
 * @param {int}    options.dx
 * @param {int}    options.dy
 * @param {int}    options.dw
 * @param {int}    options.dh
 * @param {String} options.bgColor
 * @param {Function} callback
 */
function makeImage(options, callback)
{
	// set limit resampling count
	options.resampleCount = (options.resampleCount > 0) ? options.resampleCount : 0;
	options.resampleCount = (options.resampleCount < 4) ? options.resampleCount : 4;

	var resampleMax = Math.pow(2, options.resampleCount);
	var canvas = new Canvas(options.width * resampleMax, options.height * resampleMax, options.bgColor);

	// resize canvas
	var resizeCanvas = function(count, parentCanvas)
	{
		var max = Math.pow(2, count);
		var canvasForResize = new Canvas(options.width * max, options.height * max, options.bgColor);

		canvasForResize.ctx.drawImage(parentCanvas.el, 0, 0, parentCanvas.el.width * 0.5, parentCanvas.el.height * 0.5);

		if (count > 0)
		{
			resizeCanvas(count-1, canvasForResize);
		}
		else
		{
			if (callback)
			{
				callback(canvasForResize);
			}
		}
	};

	// init draw image
	canvas.ctx.drawImage(
		options.image,
		(options.cx), // cx
		(options.cy), // cy
		(options.cw), // cw
		(options.ch), // ch
		(options.dx * resampleMax), // dx
		(options.dy * resampleMax), // dy
		(options.dw * resampleMax), // dw
		(options.dh * resampleMax) // dh
	);

	if (options.resampleCount > 0)
	{
		resizeCanvas(options.resampleCount - 1, canvas);
	}
	else
	{
		if (callback)
		{
			callback(canvas);
		}
	}
}

/**
 * Get image size
 *
 * @param {String} type
 * @param {int} cw container width
 * @param {int} ch container height
 * @param {int} iw image width
 * @param {int} ih image height
 * @return {Object}
 */
function getImageSize(type, cw, ch, iw, ih)
{
	var size  = {
		width : 0,
		height : 0
	};

	switch(type)
	{
		case 'cover':
			if (cw > ch)
			{
				size.width = cw;
				size.height = ih * (cw / iw);
				if (ch > size.height)
				{
					size.width = iw * (ch / ih);
					size.height = ch;
				}
			}
			else
			{
				size.width = iw * (ch / ih);
				size.height = ch;
				if (cw > size.width)
				{
					size.width = cw;
					size.height = ih * (cw / iw);
				}
			}
			break;

		case 'width':
			size.width = cw;
			size.height = ih * (cw / iw);
			break;

		case 'height':
			size.width = iw * (ch / ih);
			size.height = ch;
			break;

		default:
			size.width = cw;
			size.height = ch;
			break;
	}

	return {
		width : Math.round(size.width),
		height : Math.round(size.height)
	};
}

/**
 * Get image size
 *
 * @param {Object} el
 * @param {String} outputType
 * @param {int} quality
 * @return {Object}
 */
function canvasToBase64(el, outputType, quality)
{
	outputType = outputType || 'image/jpeg';
	quality = quality || 0.75;
	return el.toDataURL(outputType, (outputType == 'image/jpeg') ? quality : null);
}

module.exports = {

	container : null,
	gridster : null,

	/**
	 * init
	 *
	 * @param {object} container
	 */
	init(container)
	{
		this.container = container;
		this.gridster = container.refs.gridster;
	},

	/**
	 * Object to json
	 *
	 * @param {Object} src
	 * @param {int} space
	 * @return {String}
	 */
	objectToJson(src, space)
	{
		return JSON.stringify(src, null, (space || 4));
	},

	/**
	 * Export gridster
	 *
	 * @return {Object}
	 */
	exportGridster()
	{
		const $gridster = this.gridster.$gridster;
		const gridster = this.gridster.gridster;
		var blockData = [];

		$gridster.find('li').each((k, o) => {
			const $o = $(o);
			var data = {};

			data.color = $o.attr('data-color');

			if ($o.hasClass('attached'))
			{
				data.position = $o.children('figure').attr('data-position');
				data.size = $o.children('figure').attr('data-size');
				data.image = $o.children('figure').attr('data-image');
			}

			blockData.push(data);
		});

		return {
			params : gridster.serialize(),
			figure : blockData
		};
	},

	/**
	 * Get ratio
	 *
	 * @param {int} w
	 * @param {int} h
	 * @return {int}
	 */
	getRatio(w, h)
	{
		var result = parseInt(w) / parseInt(h);
		result = Math.round(result * 1000) / 1000;
		return result;
	},

	/**
	 * Make queue
	 *
	 * @param {Object} gridsterData
	 * @return {Array}
	 */
	makeQueue(gridsterData)
	{
		var queue = [];
		var pref = this.container.state.preference;
		var getItemSize = (width, col, margin) => {
			return (width * col) + ((col > 1) ? margin * (col - 1) : 0);
		};

		gridsterData.figure.forEach((o, k) => {
			var param = gridsterData.params[k];
			if (o.image)
			{
				queue.push({
					key : k,
					image : o.image,
					position : o.position,
					size : o.size,
					targetWidth : getItemSize(pref.width, param.size_x, pref.inner_margin),
					targetHeight : getItemSize(pref.height, param.size_y, pref.inner_margin),
					color : o.color
				});
			}
		});

		return queue;
	},

	/**
	 * Play queue
	 *
	 * @param {Array} queue
	 * @param {String} outputType
	 * @param {int} quality
	 * @param {Function} callback
	 * @return {Object}
	 */
	playQueue(queue, outputType, quality, callback)
	{
		var max = queue.length;
		var result = [];
		quality = quality || 0.75;

		function draw(key)
		{
			var img = new Image();
			var data = queue[key];

			img.onload = (e) => {
				let realSize = {
					width: img.naturalWidth,
					height: img.naturalHeight
				};
				let size = {};
				let position = {};
				let pos = [];

				if (data.size == 'cover')
				{
					size = getImageSize('cover', data.targetWidth, data.targetHeight, realSize.width, realSize.height);
					position.x = (data.targetWidth * 0.5) - (size.width * 0.5);
					position.y = (data.targetHeight * 0.5) - (size.height * 0.5);
				}
				else
				{
					pos = data.position.split(' ');
					size = getImageSize('width', data.targetWidth, data.targetHeight, realSize.width, realSize.height);
					position.x = parseInt(pos[0]);
					position.y = parseInt(pos[1]);
				}

				makeImage({
					image : img,
					resampleCount : 1,
					width : data.targetWidth,
					height : data.targetHeight,
					cx : 0,
					cy : 0,
					cw : realSize.width,
					ch : realSize.height,
					dx : position.x,
					dy : position.y,
					dw : size.width,
					dh : size.height,
					bgColor : data.color
				}, (canvas) => {
					key += 1;
					result.push({
						key : data.key,
						data : canvasToBase64(canvas.el, outputType, quality)
					});
					if (max > key)
					{
						draw(key);
					}
					else
					{
						if (callback)
						{
							callback(result);
						}
					}

				});
			};
			img.src = data.image;
		}

		if (max > 0)
		{
			draw(0);
		}
		else
		{
			if (callback)
			{
				callback(result);
			}
		}
	},

	/**
	 * Get export data
	 *
	 * @param {Object} resource
	 * @return {Object}
	 */
	getExportData(resource)
	{
		return {
			gridster : resource,
			preference : this.container.state.preference
		};
	},

	/**
	 * Draw canvas
	 *
	 * @param {Object} canvas
	 * @param {Object} gridData
	 * @param {Object} pref
	 * @param {Boolean} isImage
	 * @param {Function} callback
	 */
	drawCanvas(canvas, gridData, pref, isImage, callback)
	{
		function check(n)
		{
			if (n > 0)
			{
				play(n);
			}
			else
			{
				if (callback)
				{
					callback(canvas);
				}
			}
		}

		function play(k)
		{
			k--;

			let param = gridData.params[k];
			let figure = gridData.figure[k];
			let block = {
				x : (pref.width + pref.inner_margin) * (param.col - 1) + (pref.outer_margin + pref.inner_margin),
				y : (pref.height + pref.inner_margin) * (param.row - 1) + (pref.outer_margin + pref.inner_margin),
				width : (param.size_x * pref.width) + ((param.size_x > 1) ? (pref.inner_margin * (param.size_x-1)) : 0),
				height : (param.size_y * pref.height) + ((param.size_y > 1) ? (pref.inner_margin * (param.size_y-1)) : 0)
			};

			if (figure.image)
			{
				var img = new Image();
				img.onload = (e) => {
					canvas.ctx.drawImage(
						img,
						block.x, // dx
						block.y, // dy
						block.width, // dw
						block.height // dh
					);
					check(k);
				};
				img.src = figure.image;
			}
			else
			{
				canvas.ctx.fillStyle = (figure.color) ? figure.color : '#ffffff';
				canvas.ctx.fillRect(block.x, block.y, block.width, block.height);
				check(k);
			}
		}

		play(gridData.params.length);
	},

	/*** EXPORT METHODS ***/

	/**
	 * Basic
	 *
	 * @return {Object}
	 */
	json()
	{
		return this.objectToJson(
			this.getExportData( this.exportGridster() ),
			null
		);
	},

	/**
	 * Console
	 *
	 * @return {Object}
	 */
	console()
	{
		console.log( this.getExportData( this.exportGridster() ) );
	},

	/**
	 * Packed
	 *
	 * @param {Function} callback
	 */
	packed(callback)
	{
		var result = this.exportGridster();
		var queue = this.makeQueue(result);
		this.playQueue(queue, 'image/jpeg', 0.8, (imgResult) => {
			imgResult.forEach((o) => {
				result.figure[o.key].image = o.data;
				result.figure[o.key].position = '50% 50%';
				result.figure[o.key].size = 'cover';
			});
			if (callback)
			{
				callback( this.objectToJson(this.getExportData(result), null) );
			}
		});
	},

	/**
	 * Image
	 *
	 * @param {Function} callback
	 */
	image(callback)
	{
		var gridData = this.exportGridster();
		var pref = this.container.state.preference;
		var queue = this.makeQueue(gridData);
		var canvas = new Canvas(
			this.gridster.$gridster.width() + (pref.outer_margin * 2) + (pref.inner_margin),
			this.gridster.$gridster.height() + (pref.outer_margin * 2) + (pref.inner_margin),
			'#ffffff');

		this.playQueue(queue, 'image/png', 0, (imgResult) => {
			imgResult.forEach((o) => {
				gridData.figure[o.key].image = o.data;
				gridData.figure[o.key].position = '0 0';
				gridData.figure[o.key].size = 'cover';
			});

			this.drawCanvas(canvas, gridData, pref, (imgResult.length > 0), (canvas) => {
				if (callback)
				{
					callback(canvasToBase64(canvas.el, 'image/jpeg', 0.85));
				}
			});
		});
	}
};
<|endoftext|>


<commit_before>const React = require("react");
const translate = require("../../server/translate.js").translate;
const siteConfig = require(process.cwd() + "/siteConfig.js");

class Button extends React.Component {
  render() {
    return (

          {this.props.children}

    );
  }
}

Button.defaultProps = {
  target: "_self",
};

const PromoSection = props => (

{props.children}

);

const MiniRepl = ({ language }) => {
  return (

            <translate>Put in next-gen JavaScript</translate>

            <translate>Get browser-compatible JavaScript out</translate>


<commit_before>/* global __phantom_writeFile */
(function(global) {
    var UNDEFINED,
        exportObject;

    if (typeof module !== "undefined" && module.exports) {
        exportObject = exports;
    } else {
        exportObject = global.jasmineReporters = global.jasmineReporters || {};
    }

    function trim(str) { return str.replace(/^\s+/, "" ).replace(/\s+$/, "" ); }
    function elapsed(start, end) { return (end - start)/1000; }
    function isFailed(obj) { return obj.status === "failed"; }
    function isSkipped(obj) { return obj.status === "pending"; }
    function isDisabled(obj) { return obj.status === "disabled"; }
    function pad(n) { return n < 10 ? '0'+n : n; }
    function extend(dupe, obj) { // performs a shallow copy of all props of `obj` onto `dupe`
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                dupe[prop] = obj[prop];
            }
        }
        return dupe;
    }
    function ISODateString(d) {
        return d.getFullYear() + '-' +
            pad(d.getMonth()+1) + '-' +
            pad(d.getDate()) + 'T' +
            pad(d.getHours()) + ':' +
            pad(d.getMinutes()) + ':' +
            pad(d.getSeconds());
    }
    function escapeInvalidXmlChars(str) {
        return str.replace(/\&/g, "&")
            .replace(/</g, "<")
            .replace(/\>/g, ">")
            .replace(/\"/g, """)
            .replace(/\'/g, "'");
    }
    function getQualifiedFilename(path, filename, separator) {
        if (path && path.substr(-1) !== separator && filename.substr(0) !== separator) {
            path += separator;
        }
        return path + filename;
    }
    function log(str) {
        var con = global.console || console;
        if (con && con.log) {
            con.log(str);
        }
    }

    /**
     * A delegate for letting the consumer
     * modify the suite name when it is used inside the junit report and as a file
     * name. This is useful when running a test suite against multiple capabilities
     * because the report can have unique names for each combination of suite/spec
     * and capability/test environment.
     *
     * @callback modifySuiteName
     * @param {string} fullName
     * @param {object} suite
     */

    /**
     * Generates JUnit XML for the given spec run. There are various options
     * to control where the results are written, and the default values are
     * set to create as few .xml files as possible. It is possible to save a
     * single XML file, or an XML file for each top-level `describe`, or an
     * XML file for each `describe` regardless of nesting.
     *
     * Usage:
     *
     * jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter(options));
     *
     * @param {object} [options]
     * @param {string} [savePath] directory to save the files (default: '')
     * @param {boolean} [consolidateAll] whether to save all test results in a
     *   single file (default: true)
     *   NOTE: if true, {filePrefix} is treated as the full filename (excluding
     *     extension)
     * @param {boolean} [consolidate] whether to save nested describes within the
     *   same file as their parent (default: true)
     *   NOTE: true does nothing if consolidateAll is also true.
     *   NOTE: false also sets consolidateAll to false.
     * @param {boolean} [useDotNotation] whether to separate suite names with
     *   dots instead of spaces, ie "Class.init" not "Class init" (default: true)
     * @param {string} [filePrefix] is the string value that is prepended to the
     *   xml output file (default: junitresults-)
     *   NOTE: if consolidateAll is true, the default is simply "junitresults" and
     *     this becomes the actual filename, ie "junitresults.xml"
     * @param {string} [package] is the base package for all test suits that are
     *   handled by this report {default: none}
     * @param {function} [modifySuiteName] a delegate for letting the consumer
     *   modify the suite name when it is used inside the junit report and as a file
     *   name. This is useful when running a test suite against multiple capabilities
     *   because the report can have unique names for each combination of suite/spec
     *   and capability/test environment.
     * @param {function} [systemOut] a delegate for letting the consumer add content
     *   to a <system-out> tag as part of each <testcase> spec output. If provided,
     *   it is invoked with the spec object and the fully qualified suite as filename.
     */
    exportObject.JUnitXmlReporter = function(options) {
        var self = this;
        self.started = false;
        self.finished = false;
        // sanitize arguments
        options = options || {};
        self.savePath = options.savePath || '';
        self.consolidate = options.consolidate === UNDEFINED ? true : options.consolidate;
        self.consolidateAll = self.consolidate !== false && (options.consolidateAll === UNDEFINED ? true : options.consolidateAll);
        self.useDotNotation = options.useDotNotation === UNDEFINED ? true : options.useDotNotation;
        if (self.consolidateAll) {
            self.filePrefix = options.filePrefix || 'junitresults';
        } else {
            self.filePrefix = typeof options.filePrefix === 'string' ? options.filePrefix : 'junitresults-';
        }
        self.package = typeof(options.package) === 'string' ? escapeInvalidXmlChars(options.package) : UNDEFINED;

        if(options.modifySuiteName && typeof options.modifySuiteName !== 'function') {
            throw new Error('option "modifySuiteName" must be a function');
        }
        if(options.systemOut && typeof options.systemOut !== 'function') {
            throw new Error('option "systemOut" must be a function');
        }

        var delegates = {};
        delegates.modifySuiteName = options.modifySuiteName;
        delegates.systemOut = options.systemOut;

        var suites = [],
            currentSuite = null,
            totalSpecsExecuted = 0,
            totalSpecsDefined,
            // when use use fit, jasmine never calls suiteStarted / suiteDone, so make a fake one to use
            fakeFocusedSuite = {
                id: 'focused',
                description: 'focused specs',
                fullName: 'focused specs'
            };

        var __suites = {}, __specs = {};
        function getSuite(suite) {
            __suites[suite.id] = extend(__suites[suite.id] || {}, suite);
            return __suites[suite.id];
        }
        function getSpec(spec) {
            __specs[spec.id] = extend(__specs[spec.id] || {}, spec);
            return __specs[spec.id];
        }

        self.jasmineStarted = function(summary) {
            totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
            exportObject.startTime = new Date();
            self.started = true;
        };
        self.suiteStarted = function(suite) {
            suite = getSuite(suite);
            suite._startTime = new Date();
            suite._specs = [];
            suite._suites = [];
            suite._failures = 0;
            suite._skipped = 0;
            suite._disabled = 0;
            suite._parent = currentSuite;
            if (!currentSuite) {
                suites.push(suite);
            } else {
                currentSuite._suites.push(suite);
            }
            currentSuite = suite;
        };
        self.specStarted = function(spec) {
            if (!currentSuite) {
                // focused spec (fit) -- suiteStarted was never called
                self.suiteStarted(fakeFocusedSuite);
            }
            spec = getSpec(spec);
            spec._startTime = new Date();
            spec._suite = currentSuite;
            currentSuite._specs.push(spec);
        };
        self.specDone = function(spec) {
            spec = getSpec(spec);
            spec._endTime = new Date();
            if (isSkipped(spec)) { spec._suite._skipped++; }
            if (isDisabled(spec)) { spec._suite._disabled++; }
            if (isFailed(spec)) { spec._suite._failures += spec.failedExpectations.length; }
            totalSpecsExecuted++;
        };
        self.suiteDone = function(suite) {
            suite = getSuite(suite);
            if (suite._parent === UNDEFINED) {
                // disabled suite (xdescribe) -- suiteStarted was never called
                self.suiteStarted(suite);
            }
            suite._endTime = new Date();
            currentSuite = suite._parent;
        };
        self.jasmineDone = function() {
            if (currentSuite) {
                // focused spec (fit) -- suiteDone was never called
                self.suiteDone(fakeFocusedSuite);
            }
            var output = '';
            for (var i = 0; i < suites.length; i++) {
                output += self.getOrWriteNestedOutput(suites[i]);
            }
            // if we have anything to write here, write out the consolidated file
            if (output) {
                wrapOutputAndWriteFile(self.filePrefix, output);
            }
            //log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);

            self.finished = true;
            // this is so phantomjs-testrunner.js can tell if we're done executing
            exportObject.endTime = new Date();
        };

        self.getOrWriteNestedOutput = function(suite) {
            var output = suiteAsXml(suite);
            for (var i = 0; i < suite._suites.length; i++) {
                output += self.getOrWriteNestedOutput(suite._suites[i]);
            }
            if (self.consolidateAll || self.consolidate && suite._parent) {
                return output;
            } else {
                // if we aren't supposed to consolidate output, just write it now
                wrapOutputAndWriteFile(generateFilename(suite), output);
                return '';
            }
        };

        self.writeFile = function(filename, text) {
            var errors = [];
            var path = self.savePath;

            function phantomWrite(path, filename, text) {
                // turn filename into a qualified path
                filename = getQualifiedFilename(path, filename, window.fs_path_separator);
                // write via a method injected by phantomjs-testrunner.js
                __phantom_writeFile(filename, text);
            }

            function nodeWrite(path, filename, text) {
                var fs = require("fs");
                var nodejs_path = require("path");
                require("mkdirp").sync(path); // make sure the path exists
                var filepath = nodejs_path.join(path, filename);
                var xmlfile = fs.openSync(filepath, "w");
                fs.writeSync(xmlfile, text, 0);
                fs.closeSync(xmlfile);
                return;
            }
            // Attempt writing with each possible environment.
            // Track errors in case no write succeeds
            try {
                phantomWrite(path, filename, text);
                return;
            } catch (e) { errors.push('  PhantomJs attempt: ' + e.message); }
            try {
                nodeWrite(path, filename, text);
                return;
            } catch (f) { errors.push('  NodeJS attempt: ' + f.message); }

            // If made it here, no write succeeded.  Let user know.
            log("Warning: writing junit report failed for '" + path + "', '" +
                filename + "'. Reasons:\n" +
                errors.join("\n")
            );
        };

        /******** Helper functions with closure access for simplicity ********/
        function generateFilename(suite) {
            return self.filePrefix + getFullyQualifiedSuiteName(suite, true) + '.xml';
        }

        function getFullyQualifiedSuiteName(suite, isFilename) {
            var fullName;
            if (self.useDotNotation || isFilename) {
                fullName = suite.description;
                for (var parent = suite._parent; parent; parent = parent._parent) {
                    fullName = parent.description + '.' + fullName;
                }
            } else {
                fullName = suite.fullName;
            }

            // Either remove or escape invalid XML characters
            if (isFilename) {
                var fileName = "",
                    rFileChars = /[\w\.]/,
                    chr;
                while (fullName.length) {
                    chr = fullName[0];
                    fullName = fullName.substr(1);
                    if (rFileChars.test(chr)) {
                        fileName += chr;
                    }
                }
                return fileName;
            } else {

                if(delegates.modifySuiteName) {
                    fullName = options.modifySuiteName(fullName, suite);
                }

                return escapeInvalidXmlChars(fullName);
            }
        }

        function suiteAsXml(suite) {
            var xml = '\n <testsuite name="' + getFullyQualifiedSuiteName(suite) + '"';
            xml += ' timestamp="' + ISODateString(suite._startTime) + '"';
            xml += ' hostname="localhost"'; // many CI systems like Jenkins don't care about this, but junit spec says it is required
            xml += ' time="' + elapsed(suite._startTime, suite._endTime) + '"';
            xml += ' errors="0"';
            xml += ' tests="' + suite._specs.length + '"';
            xml += ' skipped="' + suite._skipped + '"';
            xml += ' disabled="' + suite._disabled + '"';
            // Because of JUnit's flat structure, only include directly failed tests (not failures for nested suites)
            xml += ' failures="' + suite._failures + '"';
            if (self.package) {
                xml += ' package="' + self.package + '"';
            }
            xml += '>';

            for (var i = 0; i < suite._specs.length; i++) {
                xml += specAsXml(suite._specs[i]);
            }
            xml += '\n </testsuite>';
            return xml;
        }
        function specAsXml(spec) {
            var xml = '\n  <testcase classname="' + getFullyQualifiedSuiteName(spec._suite) + '"';
            xml += ' name="' + escapeInvalidXmlChars(spec.description) + '"';
            xml += ' time="' + elapsed(spec._startTime, spec._endTime) + '"';

            var testCaseBody = '';
            if (isSkipped(spec) || isDisabled(spec)) {
                if (spec.pendingReason) {
                    testCaseBody = '\n   <skipped message="' + trim(escapeInvalidXmlChars(spec.pendingReason)) + '" />';
                } else {
                    testCaseBody = '\n   <skipped />';
                }
            } else if (isFailed(spec)) {
                for (var i = 0, failure; i < spec.failedExpectations.length; i++) {
                    failure = spec.failedExpectations[i];
                    testCaseBody += '\n   <failure type="' + (failure.matcherName || "exception") + '"';
                    testCaseBody += ' message="' + trim(escapeInvalidXmlChars(failure.message))+ '"';
                    testCaseBody += '>';
                    testCaseBody += '<![CDATA[' + trim(failure.stack || failure.message) + ']]>';
                    testCaseBody += '\n   </failure>';
                }
            }

            if (testCaseBody || delegates.systemOut) {
                xml += '>' + testCaseBody;
                if (delegates.systemOut) {
                    xml += '\n   <system-out>' + trim(escapeInvalidXmlChars(delegates.systemOut(spec, getFullyQualifiedSuiteName(spec._suite, true)))) + '</system-out>';
                }
                xml += '\n  </testcase>';
            } else {
                xml += ' />';
            }
            return xml;
        }

        // To remove complexity and be more DRY about the silly preamble and <testsuites> element
        var prefix = '<?xml version="1.0" encoding="UTF-8" ?>';
        prefix += '\n<testsuites>';
        var suffix = '\n</testsuites>';
        function wrapOutputAndWriteFile(filename, text) {
            if (filename.substr(-4) !== '.xml') { filename += '.xml'; }
            self.writeFile(filename, (prefix + text + suffix));
        }
    };
})(this);
<commit_msg>use the delegated instead option var<commit_after>/* global __phantom_writeFile */
(function(global) {
    var UNDEFINED,
        exportObject;

    if (typeof module !== "undefined" && module.exports) {
        exportObject = exports;
    } else {
        exportObject = global.jasmineReporters = global.jasmineReporters || {};
    }

    function trim(str) { return str.replace(/^\s+/, "" ).replace(/\s+$/, "" ); }
    function elapsed(start, end) { return (end - start)/1000; }
    function isFailed(obj) { return obj.status === "failed"; }
    function isSkipped(obj) { return obj.status === "pending"; }
    function isDisabled(obj) { return obj.status === "disabled"; }
    function pad(n) { return n < 10 ? '0'+n : n; }
    function extend(dupe, obj) { // performs a shallow copy of all props of `obj` onto `dupe`
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                dupe[prop] = obj[prop];
            }
        }
        return dupe;
    }
    function ISODateString(d) {
        return d.getFullYear() + '-' +
            pad(d.getMonth()+1) + '-' +
            pad(d.getDate()) + 'T' +
            pad(d.getHours()) + ':' +
            pad(d.getMinutes()) + ':' +
            pad(d.getSeconds());
    }
    function escapeInvalidXmlChars(str) {
        return str.replace(/\&/g, "&")
            .replace(/</g, "<")
            .replace(/\>/g, ">")
            .replace(/\"/g, """)
            .replace(/\'/g, "'");
    }
    function getQualifiedFilename(path, filename, separator) {
        if (path && path.substr(-1) !== separator && filename.substr(0) !== separator) {
            path += separator;
        }
        return path + filename;
    }
    function log(str) {
        var con = global.console || console;
        if (con && con.log) {
            con.log(str);
        }
    }

    /**
     * A delegate for letting the consumer
     * modify the suite name when it is used inside the junit report and as a file
     * name. This is useful when running a test suite against multiple capabilities
     * because the report can have unique names for each combination of suite/spec
     * and capability/test environment.
     *
     * @callback modifySuiteName
     * @param {string} fullName
     * @param {object} suite
     */

    /**
     * Generates JUnit XML for the given spec run. There are various options
     * to control where the results are written, and the default values are
     * set to create as few .xml files as possible. It is possible to save a
     * single XML file, or an XML file for each top-level `describe`, or an
     * XML file for each `describe` regardless of nesting.
     *
     * Usage:
     *
     * jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter(options));
     *
     * @param {object} [options]
     * @param {string} [savePath] directory to save the files (default: '')
     * @param {boolean} [consolidateAll] whether to save all test results in a
     *   single file (default: true)
     *   NOTE: if true, {filePrefix} is treated as the full filename (excluding
     *     extension)
     * @param {boolean} [consolidate] whether to save nested describes within the
     *   same file as their parent (default: true)
     *   NOTE: true does nothing if consolidateAll is also true.
     *   NOTE: false also sets consolidateAll to false.
     * @param {boolean} [useDotNotation] whether to separate suite names with
     *   dots instead of spaces, ie "Class.init" not "Class init" (default: true)
     * @param {string} [filePrefix] is the string value that is prepended to the
     *   xml output file (default: junitresults-)
     *   NOTE: if consolidateAll is true, the default is simply "junitresults" and
     *     this becomes the actual filename, ie "junitresults.xml"
     * @param {string} [package] is the base package for all test suits that are
     *   handled by this report {default: none}
     * @param {function} [modifySuiteName] a delegate for letting the consumer
     *   modify the suite name when it is used inside the junit report and as a file
     *   name. This is useful when running a test suite against multiple capabilities
     *   because the report can have unique names for each combination of suite/spec
     *   and capability/test environment.
     * @param {function} [systemOut] a delegate for letting the consumer add content
     *   to a <system-out> tag as part of each <testcase> spec output. If provided,
     *   it is invoked with the spec object and the fully qualified suite as filename.
     */
    exportObject.JUnitXmlReporter = function(options) {
        var self = this;
        self.started = false;
        self.finished = false;
        // sanitize arguments
        options = options || {};
        self.savePath = options.savePath || '';
        self.consolidate = options.consolidate === UNDEFINED ? true : options.consolidate;
        self.consolidateAll = self.consolidate !== false && (options.consolidateAll === UNDEFINED ? true : options.consolidateAll);
        self.useDotNotation = options.useDotNotation === UNDEFINED ? true : options.useDotNotation;
        if (self.consolidateAll) {
            self.filePrefix = options.filePrefix || 'junitresults';
        } else {
            self.filePrefix = typeof options.filePrefix === 'string' ? options.filePrefix : 'junitresults-';
        }
        self.package = typeof(options.package) === 'string' ? escapeInvalidXmlChars(options.package) : UNDEFINED;

        if(options.modifySuiteName && typeof options.modifySuiteName !== 'function') {
            throw new Error('option "modifySuiteName" must be a function');
        }
        if(options.systemOut && typeof options.systemOut !== 'function') {
            throw new Error('option "systemOut" must be a function');
        }

        var delegates = {};
        delegates.modifySuiteName = options.modifySuiteName;
        delegates.systemOut = options.systemOut;

        var suites = [],
            currentSuite = null,
            totalSpecsExecuted = 0,
            totalSpecsDefined,
            // when use use fit, jasmine never calls suiteStarted / suiteDone, so make a fake one to use
            fakeFocusedSuite = {
                id: 'focused',
                description: 'focused specs',
                fullName: 'focused specs'
            };

        var __suites = {}, __specs = {};
        function getSuite(suite) {
            __suites[suite.id] = extend(__suites[suite.id] || {}, suite);
            return __suites[suite.id];
        }
        function getSpec(spec) {
            __specs[spec.id] = extend(__specs[spec.id] || {}, spec);
            return __specs[spec.id];
        }

        self.jasmineStarted = function(summary) {
            totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
            exportObject.startTime = new Date();
            self.started = true;
        };
        self.suiteStarted = function(suite) {
            suite = getSuite(suite);
            suite._startTime = new Date();
            suite._specs = [];
            suite._suites = [];
            suite._failures = 0;
            suite._skipped = 0;
            suite._disabled = 0;
            suite._parent = currentSuite;
            if (!currentSuite) {
                suites.push(suite);
            } else {
                currentSuite._suites.push(suite);
            }
            currentSuite = suite;
        };
        self.specStarted = function(spec) {
            if (!currentSuite) {
                // focused spec (fit) -- suiteStarted was never called
                self.suiteStarted(fakeFocusedSuite);
            }
            spec = getSpec(spec);
            spec._startTime = new Date();
            spec._suite = currentSuite;
            currentSuite._specs.push(spec);
        };
        self.specDone = function(spec) {
            spec = getSpec(spec);
            spec._endTime = new Date();
            if (isSkipped(spec)) { spec._suite._skipped++; }
            if (isDisabled(spec)) { spec._suite._disabled++; }
            if (isFailed(spec)) { spec._suite._failures += spec.failedExpectations.length; }
            totalSpecsExecuted++;
        };
        self.suiteDone = function(suite) {
            suite = getSuite(suite);
            if (suite._parent === UNDEFINED) {
                // disabled suite (xdescribe) -- suiteStarted was never called
                self.suiteStarted(suite);
            }
            suite._endTime = new Date();
            currentSuite = suite._parent;
        };
        self.jasmineDone = function() {
            if (currentSuite) {
                // focused spec (fit) -- suiteDone was never called
                self.suiteDone(fakeFocusedSuite);
            }
            var output = '';
            for (var i = 0; i < suites.length; i++) {
                output += self.getOrWriteNestedOutput(suites[i]);
            }
            // if we have anything to write here, write out the consolidated file
            if (output) {
                wrapOutputAndWriteFile(self.filePrefix, output);
            }
            //log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);

            self.finished = true;
            // this is so phantomjs-testrunner.js can tell if we're done executing
            exportObject.endTime = new Date();
        };

        self.getOrWriteNestedOutput = function(suite) {
            var output = suiteAsXml(suite);
            for (var i = 0; i < suite._suites.length; i++) {
                output += self.getOrWriteNestedOutput(suite._suites[i]);
            }
            if (self.consolidateAll || self.consolidate && suite._parent) {
                return output;
            } else {
                // if we aren't supposed to consolidate output, just write it now
                wrapOutputAndWriteFile(generateFilename(suite), output);
                return '';
            }
        };

        self.writeFile = function(filename, text) {
            var errors = [];
            var path = self.savePath;

            function phantomWrite(path, filename, text) {
                // turn filename into a qualified path
                filename = getQualifiedFilename(path, filename, window.fs_path_separator);
                // write via a method injected by phantomjs-testrunner.js
                __phantom_writeFile(filename, text);
            }

            function nodeWrite(path, filename, text) {
                var fs = require("fs");
                var nodejs_path = require("path");
                require("mkdirp").sync(path); // make sure the path exists
                var filepath = nodejs_path.join(path, filename);
                var xmlfile = fs.openSync(filepath, "w");
                fs.writeSync(xmlfile, text, 0);
                fs.closeSync(xmlfile);
                return;
            }
            // Attempt writing with each possible environment.
            // Track errors in case no write succeeds
            try {
                phantomWrite(path, filename, text);
                return;
            } catch (e) { errors.push('  PhantomJs attempt: ' + e.message); }
            try {
                nodeWrite(path, filename, text);
                return;
            } catch (f) { errors.push('  NodeJS attempt: ' + f.message); }

            // If made it here, no write succeeded.  Let user know.
            log("Warning: writing junit report failed for '" + path + "', '" +
                filename + "'. Reasons:\n" +
                errors.join("\n")
            );
        };

        /******** Helper functions with closure access for simplicity ********/
        function generateFilename(suite) {
            return self.filePrefix + getFullyQualifiedSuiteName(suite, true) + '.xml';
        }

        function getFullyQualifiedSuiteName(suite, isFilename) {
            var fullName;
            if (self.useDotNotation || isFilename) {
                fullName = suite.description;
                for (var parent = suite._parent; parent; parent = parent._parent) {
                    fullName = parent.description + '.' + fullName;
                }
            } else {
                fullName = suite.fullName;
            }

            // Either remove or escape invalid XML characters
            if (isFilename) {
                var fileName = "",
                    rFileChars = /[\w\.]/,
                    chr;
                while (fullName.length) {
                    chr = fullName[0];
                    fullName = fullName.substr(1);
                    if (rFileChars.test(chr)) {
                        fileName += chr;
                    }
                }
                return fileName;
            } else {

                if(delegates.modifySuiteName) {
                    fullName = delegates.modifySuiteName(fullName, suite);
                }

                return escapeInvalidXmlChars(fullName);
            }
        }

        function suiteAsXml(suite) {
            var xml = '\n <testsuite name="' + getFullyQualifiedSuiteName(suite) + '"';
            xml += ' timestamp="' + ISODateString(suite._startTime) + '"';
            xml += ' hostname="localhost"'; // many CI systems like Jenkins don't care about this, but junit spec says it is required
            xml += ' time="' + elapsed(suite._startTime, suite._endTime) + '"';
            xml += ' errors="0"';
            xml += ' tests="' + suite._specs.length + '"';
            xml += ' skipped="' + suite._skipped + '"';
            xml += ' disabled="' + suite._disabled + '"';
            // Because of JUnit's flat structure, only include directly failed tests (not failures for nested suites)
            xml += ' failures="' + suite._failures + '"';
            if (self.package) {
                xml += ' package="' + self.package + '"';
            }
            xml += '>';

            for (var i = 0; i < suite._specs.length; i++) {
                xml += specAsXml(suite._specs[i]);
            }
            xml += '\n </testsuite>';
            return xml;
        }
        function specAsXml(spec) {
            var xml = '\n  <testcase classname="' + getFullyQualifiedSuiteName(spec._suite) + '"';
            xml += ' name="' + escapeInvalidXmlChars(spec.description) + '"';
            xml += ' time="' + elapsed(spec._startTime, spec._endTime) + '"';

            var testCaseBody = '';
            if (isSkipped(spec) || isDisabled(spec)) {
                if (spec.pendingReason) {
                    testCaseBody = '\n   <skipped message="' + trim(escapeInvalidXmlChars(spec.pendingReason)) + '" />';
                } else {
                    testCaseBody = '\n   <skipped />';
                }
            } else if (isFailed(spec)) {
                for (var i = 0, failure; i < spec.failedExpectations.length; i++) {
                    failure = spec.failedExpectations[i];
                    testCaseBody += '\n   <failure type="' + (failure.matcherName || "exception") + '"';
                    testCaseBody += ' message="' + trim(escapeInvalidXmlChars(failure.message))+ '"';
                    testCaseBody += '>';
                    testCaseBody += '<![CDATA[' + trim(failure.stack || failure.message) + ']]>';
                    testCaseBody += '\n   </failure>';
                }
            }

            if (testCaseBody || delegates.systemOut) {
                xml += '>' + testCaseBody;
                if (delegates.systemOut) {
                    xml += '\n   <system-out>' + trim(escapeInvalidXmlChars(delegates.systemOut(spec, getFullyQualifiedSuiteName(spec._suite, true)))) + '</system-out>';
                }
                xml += '\n  </testcase>';
            } else {
                xml += ' />';
            }
            return xml;
        }

        // To remove complexity and be more DRY about the silly preamble and <testsuites> element
        var prefix = '<?xml version="1.0" encoding="UTF-8" ?>';
        prefix += '\n<testsuites>';
        var suffix = '\n</testsuites>';
        function wrapOutputAndWriteFile(filename, text) {
            if (filename.substr(-4) !== '.xml') { filename += '.xml'; }
            self.writeFile(filename, (prefix + text + suffix));
        }
    };
})(this);
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Text, View } from 'react-native'
import * as d3Scale from 'd3-scale'
import * as array from 'd3-array'
import Svg, { Text as SVGText } from 'react-native-svg'

class XAxis extends PureComponent {

    state = {
        width: 0,
        height: 0,
    }

    _onLayout(event) {
        const { nativeEvent: { layout: { width, height } } } = event

        if (width !== this.state.width) {
            this.setState({ width, height })
        }
    }

    _getX(domain) {
        const {
            scale,
            spacingInner,
            spacingOuter,
            contentInset: {
                left  = 0,
                right = 0,
            },
        } = this.props

        const { width } = this.state

        const x = scale()
            .domain(domain)
            .range([ left, width - right ])

        if (scale === d3Scale.scaleBand) {

            x
                .paddingInner([ spacingInner ])
                .paddingOuter([ spacingOuter ])

            //add half a bar to center label
            return (value) => x(value) + (x.bandwidth() / 2)
        }

        return x
    }

    render() {

        const {
            style,
            scale,
            data,
            xAccessor,
            formatLabel,
            numberOfTicks,
            svg,
            children,
            min,
            max,
        } = this.props

        const { height, width } = this.state

        if (data.length === 0) {
            return <View style={ style }/>
        }

        const values = data.map((item, index) => xAccessor({ item, index }))
        const extent  = array.extent(values)
        const domain = scale === d3Scale.scaleBand ?
            values :
            [ min || extent[ 0 ], max || extent[ 1 ] ]

        const x     = this._getX(domain)
        const ticks = numberOfTicks ? x.ticks(numberOfTicks) : values

        return (
            <View style={ style }>
                <View
                    style={{ flexGrow: 1 }}
                    onLayout={ event => this._onLayout(event) }
                >
                    {/*invisible text to allow for parent resizing*/}
                    <Text style={{ color: 'transparent', fontSize: svg.fontSize }}>
                        { formatLabel(ticks[0], 0) }
                    </Text>
                    {
                        height > 0 && width > 0 &&
                        <Svg style={{
                            position: 'absolute',
                            top: 0,
                            left: 0,
                            height,
                            width,
                        }}>
                            {children}
                            {
                                // don't render labels if width isn't measured yet,
                                // causes rendering issues
                                width > 0 &&
                                ticks.map((value, index) => {
                                    const { svg: valueSvg = {} } = data[ index ] || {}

                                    return (
                                        <SVGText
                                            textAnchor={ 'middle' }
                                            originX={ x(value) }
                                            alignmentBaseline={ 'hanging' }
                                            { ...svg }
                                            { ...valueSvg }
                                            key={ index }
                                            x={ x(value) }
                                        >
                                            {formatLabel(value, index)}
                                        </SVGText>
                                    )
                                })
                            }
                        </Svg>
                    }
                </View>
            </View>
        )
    }
}

XAxis.propTypes = {
    data: PropTypes.arrayOf(PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.object,
    ])).isRequired,
    spacingInner: PropTypes.number,
    spacingOuter: PropTypes.number,
    formatLabel: PropTypes.func,
    contentInset: PropTypes.shape({
        left: PropTypes.number,
        right: PropTypes.number,
    }),
    scale: PropTypes.oneOf([ d3Scale.scaleTime, d3Scale.scaleLinear, d3Scale.scaleBand ]),
    numberOfTicks: PropTypes.number,
    xAccessor: PropTypes.func,
    svg: PropTypes.object,
    min: PropTypes.any,
    max: PropTypes.any,
}

XAxis.defaultProps = {
    spacingInner: 0.05,
    spacingOuter: 0.05,
    contentInset: {},
    svg: {},
    xAccessor: ({ index }) => index,
    scale: d3Scale.scaleLinear,
    formatLabel: value => value,
}

export default XAxis
<commit_msg>Fix 'transparent' color for X-Axis in RN 0.57<commit_after>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Text, View } from 'react-native'
import * as d3Scale from 'd3-scale'
import * as array from 'd3-array'
import Svg, { Text as SVGText } from 'react-native-svg'

class XAxis extends PureComponent {

    state = {
        width: 0,
        height: 0,
    }

    _onLayout(event) {
        const { nativeEvent: { layout: { width, height } } } = event

        if (width !== this.state.width) {
            this.setState({ width, height })
        }
    }

    _getX(domain) {
        const {
            scale,
            spacingInner,
            spacingOuter,
            contentInset: {
                left  = 0,
                right = 0,
            },
        } = this.props

        const { width } = this.state

        const x = scale()
            .domain(domain)
            .range([ left, width - right ])

        if (scale === d3Scale.scaleBand) {

            x
                .paddingInner([ spacingInner ])
                .paddingOuter([ spacingOuter ])

            //add half a bar to center label
            return (value) => x(value) + (x.bandwidth() / 2)
        }

        return x
    }

    render() {

        const {
            style,
            scale,
            data,
            xAccessor,
            formatLabel,
            numberOfTicks,
            svg,
            children,
            min,
            max,
        } = this.props

        const { height, width } = this.state

        if (data.length === 0) {
            return <View style={ style }/>
        }

        const values = data.map((item, index) => xAccessor({ item, index }))
        const extent  = array.extent(values)
        const domain = scale === d3Scale.scaleBand ?
            values :
            [ min || extent[ 0 ], max || extent[ 1 ] ]

        const x     = this._getX(domain)
        const ticks = numberOfTicks ? x.ticks(numberOfTicks) : values

        return (
            <View style={ style }>
                <View
                    style={{ flexGrow: 1 }}
                    onLayout={ event => this._onLayout(event) }
                >
                    {/*invisible text to allow for parent resizing*/}
                    <Text style={{ opacity: 0, fontSize: svg.fontSize }}>
                        { formatLabel(ticks[0], 0) }
                    </Text>
                    {
                        height > 0 && width > 0 &&
                        <Svg style={{
                            position: 'absolute',
                            top: 0,
                            left: 0,
                            height,
                            width,
                        }}>
                            {children}
                            {
                                // don't render labels if width isn't measured yet,
                                // causes rendering issues
                                width > 0 &&
                                ticks.map((value, index) => {
                                    const { svg: valueSvg = {} } = data[ index ] || {}

                                    return (
                                        <SVGText
                                            textAnchor={ 'middle' }
                                            originX={ x(value) }
                                            alignmentBaseline={ 'hanging' }
                                            { ...svg }
                                            { ...valueSvg }
                                            key={ index }
                                            x={ x(value) }
                                        >
                                            {formatLabel(value, index)}
                                        </SVGText>
                                    )
                                })
                            }
                        </Svg>
                    }
                </View>
            </View>
        )
    }
}

XAxis.propTypes = {
    data: PropTypes.arrayOf(PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.object,
    ])).isRequired,
    spacingInner: PropTypes.number,
    spacingOuter: PropTypes.number,
    formatLabel: PropTypes.func,
    contentInset: PropTypes.shape({
        left: PropTypes.number,
        right: PropTypes.number,
    }),
    scale: PropTypes.oneOf([ d3Scale.scaleTime, d3Scale.scaleLinear, d3Scale.scaleBand ]),
    numberOfTicks: PropTypes.number,
    xAccessor: PropTypes.func,
    svg: PropTypes.object,
    min: PropTypes.any,
    max: PropTypes.any,
}

XAxis.defaultProps = {
    spacingInner: 0.05,
    spacingOuter: 0.05,
    contentInset: {},
    svg: {},
    xAccessor: ({ index }) => index,
    scale: d3Scale.scaleLinear,
    formatLabel: value => value,
}

export default XAxis
<|endoftext|>


<commit_before>var Dota2 = require("../index"),
    util = require("util");

var cacheTypeIDs = {
  LOBBY: 2004,
  PARTY: 2003,
  PARTY_INVITE: 2006
};

// Handlers
function handleSubscribedType(obj_type, object_data)
{
  switch(obj_type)
  {
    // Lobby snapshot.
    case cacheTypeIDs.LOBBY:
      var lobby = Dota2.schema.CSODOTALobby.decode(object_data);
      if(this.debug) util.log("Received lobby snapshot for lobby ID "+lobby.lobby_id);
      this.emit("practiceLobbyUpdate", lobby);
      this.Lobby = lobby;
      break;
    // Party snapshot.
    case cacheTypeIDs.PARTY:
      var party = Dota2.schema.CSODOTAParty.decode(object_data);
      if(this.debug) util.log("Received party snapshot for party ID "+party.party_id);
      this.emit("partyUpdate", party);
      this.Party = party;
      break;
    // Party invite snapshot.
    case cacheTypeIDs.PARTY_INVITE:
      var party = Dota2.schema.CSODOTAPartyInvite.decode(object_data);
      if(this.debug) util.log("Received party invite snapshot for group ID "+party.group_id);
      this.emit("partyInviteUpdate", party);
      this.PartyInvite = party;
      break;
    default:
      if(this.debug) util.log("Unknown cache ID: "+obj_type);
      break;
  }
};

Dota2.Dota2Client.prototype._handleWelcomeCaches = function handleWelcomeCaches(message)
{
  var welcome = Dota2.schema.CMsgClientWelcome.decode(message);
  var _self = this;

  if(welcome.outofdate_subscribed_caches)
    welcome.outofdate_subscribed_caches.forEach(function(cache){
      cache.objects.forEach(function(obj){
        handleSubscribedType.call(_self, obj.type_id, obj.object_data[0]);
      });
    });
};

var handlers = Dota2.Dota2Client.prototype._handlers;

var onCacheSubscribed = function onCacheSubscribed(message) {
  var subscribe = Dota2.schema.CMsgSOCacheSubscribed.decode(message);
  var _self = this;

  if(this.debug){
    util.log("Cache subscribed, type "+subscribe.objects[0].type_id);
  }

  subscribe.objects.forEach(function(obj){
    handleSubscribedType.call(_self, obj.type_id, obj.object_data[0]);
  });
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_CacheSubscribed] = onCacheSubscribed;

var onUpdateMultiple = function onUpdateMultiple(message) {
  var multi = Dota2.schema.CMsgSOMultipleObjects.decode(message);
  var _self = this;

  if(multi.objects_modified)
    multi.objects_modified.forEach(function(obj){
      handleSubscribedType.call(_self, obj.type_id, obj.object_data[0]);
    });
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_UpdateMultiple] = onUpdateMultiple;

var onCreate = function onCreate(message) {
  var single = Dota2.schema.CMsgSOSingleObject.decode(message);
  var _self = this;

  if(this.debug){
    util.log("Create, type "+single.type_id);
  }
  handleSubscribedType.call(_self, single.type_id, single.object_data);
}
handlers[Dota2.schema.ESOMsg.k_ESOMsg_Create] = onCreate;

var onCacheUnsubscribed = function onCacheUnsubscribed(message) {
  var unsubscribe = Dota2.schema.CMsgSOCacheUnsubscribed.decode(message);
  var _self = this;

  if(this.debug) util.log("Cache unsubscribed, "+unsubscribe.owner_soid.id);

  if(this.Lobby && ""+unsubscribe.owner_soid.id === ""+this.Lobby.lobby_id)
  {
    this.Lobby = null;
    this.emit("practiceLobbyCleared");
  }else if(this.Party && ""+unsubscribe.owner_soid.id === ""+this.Party.party_id)
  {
    this.Party = null;
    this.emit("partyCleared");
  }else if(this.PartyInvite && ""+unsubscribe.owner_soid.id === ""+this.PartyInvite.group_id)
  {
    this.PartyInvite = null;
    this.emit("partyInviteCleared");
  }
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_CacheUnsubscribed] = onCacheUnsubscribed;

var onCacheDestroy = function onCacheDestroy(message) {
  var destroy = Dota2.schema.CMsgSOSingleObject.decode(message);
  var _self = this;

  if(this.debug) util.log("Cache destroy, "+destroy.type_id);

  if(destroy.type_id === cacheTypeIDs.PARTY_INVITE)
  {
    this.PartyInvite = null;
    this.emit("partyInviteCleared");
  }
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_CacheDestroy] = onCacheDestroy;
<commit_msg>Revert "Stringified comparison to properly clean up lobbies/parties and fire the cleared events"<commit_after>var Dota2 = require("../index"),
    util = require("util");

var cacheTypeIDs = {
  LOBBY: 2004,
  PARTY: 2003,
  PARTY_INVITE: 2006
};

// Handlers
function handleSubscribedType(obj_type, object_data)
{
  switch(obj_type)
  {
    // Lobby snapshot.
    case cacheTypeIDs.LOBBY:
      var lobby = Dota2.schema.CSODOTALobby.decode(object_data);
      if(this.debug) util.log("Received lobby snapshot for lobby ID "+lobby.lobby_id);
      this.emit("practiceLobbyUpdate", lobby);
      this.Lobby = lobby;
      break;
    // Party snapshot.
    case cacheTypeIDs.PARTY:
      var party = Dota2.schema.CSODOTAParty.decode(object_data);
      if(this.debug) util.log("Received party snapshot for party ID "+party.party_id);
      this.emit("partyUpdate", party);
      this.Party = party;
      break;
    // Party invite snapshot.
    case cacheTypeIDs.PARTY_INVITE:
      var party = Dota2.schema.CSODOTAPartyInvite.decode(object_data);
      if(this.debug) util.log("Received party invite snapshot for group ID "+party.group_id);
      this.emit("partyInviteUpdate", party);
      this.PartyInvite = party;
      break;
    default:
      if(this.debug) util.log("Unknown cache ID: "+obj_type);
      break;
  }
};

Dota2.Dota2Client.prototype._handleWelcomeCaches = function handleWelcomeCaches(message)
{
  var welcome = Dota2.schema.CMsgClientWelcome.decode(message);
  var _self = this;

  if(welcome.outofdate_subscribed_caches)
    welcome.outofdate_subscribed_caches.forEach(function(cache){
      cache.objects.forEach(function(obj){
        handleSubscribedType.call(_self, obj.type_id, obj.object_data[0]);
      });
    });
};

var handlers = Dota2.Dota2Client.prototype._handlers;

var onCacheSubscribed = function onCacheSubscribed(message) {
  var subscribe = Dota2.schema.CMsgSOCacheSubscribed.decode(message);
  var _self = this;

  if(this.debug){
    util.log("Cache subscribed, type "+subscribe.objects[0].type_id);
  }

  subscribe.objects.forEach(function(obj){
    handleSubscribedType.call(_self, obj.type_id, obj.object_data[0]);
  });
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_CacheSubscribed] = onCacheSubscribed;

var onUpdateMultiple = function onUpdateMultiple(message) {
  var multi = Dota2.schema.CMsgSOMultipleObjects.decode(message);
  var _self = this;

  if(multi.objectsModified)
    multi.objectsModified.forEach(function(obj){
      handleSubscribedType.call(_self, obj.type_id, obj.object_data[0]);
    });
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_UpdateMultiple] = onUpdateMultiple;

var onCreate = function onCreate(message) {
  var single = Dota2.schema.CMsgSOSingleObject.decode(message);
  var _self = this;

  if(this.debug){
    util.log("Create, type "+single.type_id);
  }
  handleSubscribedType.call(_self, single.type_id, single.object_data);
}
handlers[Dota2.schema.ESOMsg.k_ESOMsg_Create] = onCreate;

var onCacheUnsubscribed = function onCacheUnsubscribed(message) {
  var unsubscribe = Dota2.schema.CMsgSOCacheUnsubscribed.decode(message);
  var _self = this;

  if(this.debug) util.log("Cache unsubscribed, "+unsubscribe.owner_soid.id);

  if(this.Lobby && unsubscribe.owner_soid.id === this.Lobby.lobby_id)
  {
    this.Lobby = null;
    this.emit("practiceLobbyCleared");
  }else if(this.Party && unsubscribe.owner_soid.id === this.Party.party_id)
  {
    this.Party = null;
    this.emit("partyCleared");
  }else if(this.PartyInvite && unsubscribe.owner_soid.id === this.PartyInvite.group_id)
  {
    this.PartyInvite = null;
    this.emit("partyInviteCleared");
  }
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_CacheUnsubscribed] = onCacheUnsubscribed;

var onCacheDestroy = function onCacheDestroy(message) {
  var destroy = Dota2.schema.CMsgSOSingleObject.decode(message);
  var _self = this;

  if(this.debug) util.log("Cache destroy, "+destroy.type_id);

  if(destroy.type_id === cacheTypeIDs.PARTY_INVITE)
  {
    this.PartyInvite = null;
    this.emit("partyInviteCleared");
  }
};
handlers[Dota2.schema.ESOMsg.k_ESOMsg_CacheDestroy] = onCacheDestroy;
<|endoftext|>


<commit_before>// ==========================================================================
// Project:  Ember Metal
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================

require('ember-metal/core');
require('ember-metal/platform');
require('ember-metal/utils');

var META_KEY = Ember.META_KEY, get, set;

var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;

// ..........................................................
// GET AND SET
//
// If we are on a platform that supports accessors we can get use those.
// Otherwise simulate accessors by looking up the property directly on the
// object.

/** @private */
get = function get(obj, keyName) {
  Ember.assert("You need to provide an object and key to `get`.", !!obj && keyName);
  var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret;
  if (desc) {
    return desc.get(obj, keyName);
  } else {
    var isUnknown = 'object' === typeof obj && !(keyName in obj);

    if (isUnknown && 'function' === typeof obj.unknownProperty) {
      return obj.unknownProperty(keyName);
    }

    if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { return meta.values[keyName]; }
    return obj[keyName];
  }
};

/** @private */
set = function set(obj, keyName, value) {
  Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined);

  if (obj.isDestroyed) return;

  var meta = obj[META_KEY], desc = meta && meta.descs[keyName];
  if (desc) {
    desc.set(obj, keyName, value);
  }
  else {
    var isUnknown = 'object' === typeof obj && !(keyName in obj);

    // setUnknownProperty is called if `obj` is an object,
    // the property does not already exist, and the
    // `setUnknownProperty` method exists on the object
    if (isUnknown && 'function' === typeof obj.setUnknownProperty) {
      obj.setUnknownProperty(keyName, value);
    } else if (meta && meta.watching[keyName] > 0) {
      // only trigger a change if the value has changed
      if (value !== obj[keyName]) {
        Ember.propertyWillChange(obj, keyName);
        if (MANDATORY_SETTER) {
          meta.values[keyName] = value;
        } else {
          obj[keyName] = value;
        }
        Ember.propertyDidChange(obj, keyName);
      }
    } else {
      obj[keyName] = value;
    }
  }
  return value;
};

/**
  @function

  Gets the value of a property on an object.  If the property is computed,
  the function will be invoked.  If the property is not defined but the
  object implements the unknownProperty() method then that will be invoked.

  If you plan to run on IE8 and older browsers then you should use this
  method anytime you want to retrieve a property on an object that you don't
  know for sure is private.  (My convention only properties beginning with
  an underscore '_' are considered private.)

  On all newer browsers, you only need to use this method to retrieve
  properties if the property might not be defined on the object and you want
  to respect the unknownProperty() handler.  Otherwise you can ignore this
  method.

  Note that if the obj itself is null, this method will simply return
  undefined.

  @param {Object} obj
    The object to retrieve from.

  @param {String} keyName
    The property key to retrieve

  @returns {Object} the property value or null.
*/
Ember.get = get;

/**
  @function

  Sets the value of a property on an object, respecting computed properties
  and notifying observers and other listeners of the change.  If the
  property is not defined but the object implements the unknownProperty()
  method then that will be invoked as well.

  If you plan to run on IE8 and older browsers then you should use this
  method anytime you want to set a property on an object that you don't
  know for sure is private.  (My convention only properties beginning with
  an underscore '_' are considered private.)

  On all newer browsers, you only need to use this method to set
  properties if the property might not be defined on the object and you want
  to respect the unknownProperty() handler.  Otherwise you can ignore this
  method.

  @param {Object} obj
    The object to modify.

  @param {String} keyName
    The property key to set

  @param {Object} value
    The value to set

  @returns {Object} the passed value.
*/
Ember.set = set;

if (Ember.config.overrideAccessors) {
  Ember.config.overrideAccessors();
  get = Ember.get;
  set = Ember.set;
}

// ..........................................................
// PATHS
//

/** @private */
function getPath(target, path) {
  var parts = path.split(".");
  for (var i=0, l=parts.length; target && i<l; i++) {
    target = get(target, parts[i]);
    if (target && target.isDestroyed) { return undefined; }
  }
  return target;
}

var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/;
var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/;
var HAS_THIS  = /^this[\.\*]/;
var FIRST_KEY = /^([^\.\*]+)/;

/** @private */
function firstKey(path) {
  return path.match(FIRST_KEY)[0];
}

// assumes path is already normalized
/** @private */
function normalizeTuple(target, path) {
  var hasThis  = HAS_THIS.test(path),
      isGlobal = !hasThis && IS_GLOBAL_PATH.test(path),
      key;

  if (!target || isGlobal) target = window;
  if (hasThis) path = path.slice(5);

  if (target === window) {
    key = firstKey(path);
    target = get(target, key);
    path   = path.slice(key.length+1);
  }

  // must return some kind of path to be valid else other things will break.
  if (!path || path.length===0) throw new Error('Invalid Path');

  return [ target, path ];
}

/**
  @private

  Normalizes a target/path pair to reflect that actual target/path that should
  be observed, etc.  This takes into account passing in global property
  paths (i.e. a path beginning with a captial letter not defined on the
  target) and * separators.

  @param {Object} target
    The current target.  May be null.

  @param {String} path
    A path on the target or a global property path.

  @returns {Array} a temporary array with the normalized target/path pair.
*/
Ember.normalizeTuple = function(target, path) {
  return normalizeTuple(target, path);
};

Ember.normalizeTuple.primitive = normalizeTuple;

Ember.getWithDefault = function(root, key, defaultValue) {
  var value = Ember.get(root, key);

  if (value === undefined) { return defaultValue; }
  return value;
};

Ember.getPath = function(root, path) {
  var hasThis, isGlobal, ret;

  // Helpers that operate with 'this' within an #each
  if (path === '') {
    return root;
  }

  if (!path && 'string'===typeof root) {
    path = root;
    root = null;
  }

  // If there is no root and path is a key name, return that
  // property from the global object.
  // E.g. getPath('Ember') -> Ember
  if (root === null && path.indexOf('.') < 0) { return get(window, path); }

  // detect complicated paths and normalize them
  hasThis  = HAS_THIS.test(path);

  if (!root || hasThis) {
    var tuple = normalizeTuple(root, path);
    root = tuple[0];
    path = tuple[1];
    tuple.length = 0;
  }

  return getPath(root, path);
};

Ember.setPath = function(root, path, value, tolerant) {
  var keyName;

  if (typeof root === 'string') {
    Ember.assert("Path '" + root + "' must be global if no root is given.", IS_GLOBAL.test(root));
    value = path;
    path = root;
    root = null;
  }

  if (path.indexOf('.') > 0) {
    // get the last part of the path
    keyName = path.slice(path.lastIndexOf('.') + 1);

    // get the first part of the part
    path    = path.slice(0, path.length-(keyName.length+1));

    // unless the path is this, look up the first part to
    // get the root
    if (path !== 'this') {
      root = Ember.getPath(root, path);
    }
  } else {
    Ember.assert("A global path passed to setPath must have at least one period", !IS_GLOBAL.test(path) || path.indexOf(".") > -1);
    keyName = path;
  }

  if (!keyName || keyName.length === 0) {
    throw new Error('You passed an empty path');
  }

  if (!root) {
    if (tolerant) { return; }
    else { throw new Error('Object in path '+path+' could not be found or was destroyed.'); }
  }

  return Ember.set(root, keyName, value);
};

/**
  Error-tolerant form of Ember.setPath. Will not blow up if any part of the
  chain is undefined, null, or destroyed.

  This is primarily used when syncing bindings, which may try to update after
  an object has been destroyed.
*/
Ember.trySetPath = function(root, path, value) {
  return Ember.setPath(root, path, value, true);
};

/**
  Returns true if the provided path is global (e.g., "MyApp.fooController.bar")
  instead of local ("foo.bar.baz").

  @param {String} path
  @returns Boolean
*/
Ember.isGlobalPath = function(path) {
  return IS_GLOBAL.test(path);
};
<commit_msg>check MANDATORY_SETTER when getting the current value and move vars to top<commit_after>// ==========================================================================
// Project:  Ember Metal
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================

require('ember-metal/core');
require('ember-metal/platform');
require('ember-metal/utils');

var META_KEY = Ember.META_KEY, get, set;

var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;

// ..........................................................
// GET AND SET
//
// If we are on a platform that supports accessors we can get use those.
// Otherwise simulate accessors by looking up the property directly on the
// object.

/** @private */
get = function get(obj, keyName) {
  Ember.assert("You need to provide an object and key to `get`.", !!obj && keyName);
  var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret;
  if (desc) {
    return desc.get(obj, keyName);
  } else {
    var isUnknown = 'object' === typeof obj && !(keyName in obj);

    if (isUnknown && 'function' === typeof obj.unknownProperty) {
      return obj.unknownProperty(keyName);
    }

    if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { return meta.values[keyName]; }
    return obj[keyName];
  }
};

/** @private */
set = function set(obj, keyName, value) {
  Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined);

  if (obj.isDestroyed) return;

  var meta = obj[META_KEY], desc = meta && meta.descs[keyName],
      isUnknown, currentValue;
  if (desc) {
    desc.set(obj, keyName, value);
  }
  else {
    isUnknown = 'object' === typeof obj && !(keyName in obj);

    // setUnknownProperty is called if `obj` is an object,
    // the property does not already exist, and the
    // `setUnknownProperty` method exists on the object
    if (isUnknown && 'function' === typeof obj.setUnknownProperty) {
      obj.setUnknownProperty(keyName, value);
    } else if (meta && meta.watching[keyName] > 0) {
      if (MANDATORY_SETTER) {
        currentValue = meta.values[keyName];
      } else {
        currentValue = obj[keyName];
      }
      // only trigger a change if the value has changed
      if (value !== currentValue) {
        Ember.propertyWillChange(obj, keyName);
        if (MANDATORY_SETTER) {
          meta.values[keyName] = value;
        } else {
          obj[keyName] = value;
        }
        Ember.propertyDidChange(obj, keyName);
      }
    } else {
      obj[keyName] = value;
    }
  }
  return value;
};

/**
  @function

  Gets the value of a property on an object.  If the property is computed,
  the function will be invoked.  If the property is not defined but the
  object implements the unknownProperty() method then that will be invoked.

  If you plan to run on IE8 and older browsers then you should use this
  method anytime you want to retrieve a property on an object that you don't
  know for sure is private.  (My convention only properties beginning with
  an underscore '_' are considered private.)

  On all newer browsers, you only need to use this method to retrieve
  properties if the property might not be defined on the object and you want
  to respect the unknownProperty() handler.  Otherwise you can ignore this
  method.

  Note that if the obj itself is null, this method will simply return
  undefined.

  @param {Object} obj
    The object to retrieve from.

  @param {String} keyName
    The property key to retrieve

  @returns {Object} the property value or null.
*/
Ember.get = get;

/**
  @function

  Sets the value of a property on an object, respecting computed properties
  and notifying observers and other listeners of the change.  If the
  property is not defined but the object implements the unknownProperty()
  method then that will be invoked as well.

  If you plan to run on IE8 and older browsers then you should use this
  method anytime you want to set a property on an object that you don't
  know for sure is private.  (My convention only properties beginning with
  an underscore '_' are considered private.)

  On all newer browsers, you only need to use this method to set
  properties if the property might not be defined on the object and you want
  to respect the unknownProperty() handler.  Otherwise you can ignore this
  method.

  @param {Object} obj
    The object to modify.

  @param {String} keyName
    The property key to set

  @param {Object} value
    The value to set

  @returns {Object} the passed value.
*/
Ember.set = set;

if (Ember.config.overrideAccessors) {
  Ember.config.overrideAccessors();
  get = Ember.get;
  set = Ember.set;
}

// ..........................................................
// PATHS
//

/** @private */
function getPath(target, path) {
  var parts = path.split(".");
  for (var i=0, l=parts.length; target && i<l; i++) {
    target = get(target, parts[i]);
    if (target && target.isDestroyed) { return undefined; }
  }
  return target;
}

var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/;
var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/;
var HAS_THIS  = /^this[\.\*]/;
var FIRST_KEY = /^([^\.\*]+)/;

/** @private */
function firstKey(path) {
  return path.match(FIRST_KEY)[0];
}

// assumes path is already normalized
/** @private */
function normalizeTuple(target, path) {
  var hasThis  = HAS_THIS.test(path),
      isGlobal = !hasThis && IS_GLOBAL_PATH.test(path),
      key;

  if (!target || isGlobal) target = window;
  if (hasThis) path = path.slice(5);

  if (target === window) {
    key = firstKey(path);
    target = get(target, key);
    path   = path.slice(key.length+1);
  }

  // must return some kind of path to be valid else other things will break.
  if (!path || path.length===0) throw new Error('Invalid Path');

  return [ target, path ];
}

/**
  @private

  Normalizes a target/path pair to reflect that actual target/path that should
  be observed, etc.  This takes into account passing in global property
  paths (i.e. a path beginning with a captial letter not defined on the
  target) and * separators.

  @param {Object} target
    The current target.  May be null.

  @param {String} path
    A path on the target or a global property path.

  @returns {Array} a temporary array with the normalized target/path pair.
*/
Ember.normalizeTuple = function(target, path) {
  return normalizeTuple(target, path);
};

Ember.normalizeTuple.primitive = normalizeTuple;

Ember.getWithDefault = function(root, key, defaultValue) {
  var value = Ember.get(root, key);

  if (value === undefined) { return defaultValue; }
  return value;
};

Ember.getPath = function(root, path) {
  var hasThis, isGlobal, ret;

  // Helpers that operate with 'this' within an #each
  if (path === '') {
    return root;
  }

  if (!path && 'string'===typeof root) {
    path = root;
    root = null;
  }

  // If there is no root and path is a key name, return that
  // property from the global object.
  // E.g. getPath('Ember') -> Ember
  if (root === null && path.indexOf('.') < 0) { return get(window, path); }

  // detect complicated paths and normalize them
  hasThis  = HAS_THIS.test(path);

  if (!root || hasThis) {
    var tuple = normalizeTuple(root, path);
    root = tuple[0];
    path = tuple[1];
    tuple.length = 0;
  }

  return getPath(root, path);
};

Ember.setPath = function(root, path, value, tolerant) {
  var keyName;

  if (typeof root === 'string') {
    Ember.assert("Path '" + root + "' must be global if no root is given.", IS_GLOBAL.test(root));
    value = path;
    path = root;
    root = null;
  }

  if (path.indexOf('.') > 0) {
    // get the last part of the path
    keyName = path.slice(path.lastIndexOf('.') + 1);

    // get the first part of the part
    path    = path.slice(0, path.length-(keyName.length+1));

    // unless the path is this, look up the first part to
    // get the root
    if (path !== 'this') {
      root = Ember.getPath(root, path);
    }
  } else {
    Ember.assert("A global path passed to setPath must have at least one period", !IS_GLOBAL.test(path) || path.indexOf(".") > -1);
    keyName = path;
  }

  if (!keyName || keyName.length === 0) {
    throw new Error('You passed an empty path');
  }

  if (!root) {
    if (tolerant) { return; }
    else { throw new Error('Object in path '+path+' could not be found or was destroyed.'); }
  }

  return Ember.set(root, keyName, value);
};

/**
  Error-tolerant form of Ember.setPath. Will not blow up if any part of the
  chain is undefined, null, or destroyed.

  This is primarily used when syncing bindings, which may try to update after
  an object has been destroyed.
*/
Ember.trySetPath = function(root, path, value) {
  return Ember.setPath(root, path, value, true);
};

/**
  Returns true if the provided path is global (e.g., "MyApp.fooController.bar")
  instead of local ("foo.bar.baz").

  @param {String} path
  @returns Boolean
*/
Ember.isGlobalPath = function(path) {
  return IS_GLOBAL.test(path);
};
<|endoftext|>


<commit_before><commit_msg>we'll use the .browserslistrc config in uppy root instead<commit_after><|endoftext|>


<commit_before>// Woodlot custom logging module

var foregroundYellow = require('./stdoutColors').foregroundYellow,
	underlineText = require('./stdoutColors').underlineText,
	logToStream = require('./fileStreamHandler'),
	stringifyLog = require('./utils').stringifyLog,
	DEFAULT_JSON_SPACING = require('./constants').DEFAULT_JSON_SPACING,
	generateCLFTimestamp = require('./utils').generateCLFTimestamp,
	customLoggerConfig = null;

var customLogger = {
	config: function(config) {
		if(!config || !config.streams) {
	        console.log(foregroundYellow('Woodlot warning: Please provide at least one valid file stream to start logging. More info here - ' + underlineText('https://github.com/adpushup/woodlot')));
	        return;
	    } else {
	    	config.logToConsole = ('stdOut' in config) ? config.stdOut : true;
	    	customLoggerConfig = config;
	    }
	},
	log: function(message) {
		var config = customLoggerConfig
			log = {
				timeStamp: generateCLFTimestamp(new Date()),
				message: message
			}

		// Write to specified file streams
        logToStream(stringifyLog(log, 'json', DEFAULT_JSON_SPACING), config);

		// Write request log to stdout if option is present
        config.logToConsole ? console.log(stringifyLog(log, 'json', DEFAULT_JSON_SPACING, config)) : null;
	}
};

module.exports = customLogger;<commit_msg>Added support for format spacing and separator in custom logger<commit_after>// Woodlot custom logging module

var foregroundYellow = require('./stdoutColors').foregroundYellow,
    underlineText = require('./stdoutColors').underlineText,
    logToStream = require('./fileStreamHandler'),
    stringifyLog = require('./utils').stringifyLog,
    DEFAULT_JSON_SPACING = require('./constants').DEFAULT_JSON_SPACING,
    DEFAULT_SEPARATOR = require('./constants').DEFAULT_SEPARATOR,
    generateCLFTimestamp = require('./utils').generateCLFTimestamp,
    getLogFormat = require('./utils').getLogFormat,
    getFormatSpacing = require('./utils').getFormatSpacing,
    getFormatSeparator = require('./utils').getFormatSeparator,
    customLoggerConfig = null;

var customLogger = {
    config: function(config) {
        if (!config || !config.streams) {
            console.log(foregroundYellow('Woodlot warning: Please provide at least one valid file stream to start logging. More info here - ' + underlineText('https://github.com/adpushup/woodlot')));
            return;
        } else {
            config.logToConsole = ('stdOut' in config) ? config.stdOut : true;
            config.format = {
                type: 'json',
                options: {
                    spacing: config.spacing ? config.spacing : DEFAULT_JSON_SPACING,
                    separator: config.separator ? config.separator : DEFAULT_SEPARATOR
                }
            };

            customLoggerConfig = config;
        }
    },
    log: function(message) {
        var config = customLoggerConfig
        log = {
                timeStamp: generateCLFTimestamp(new Date()),
                message: message
            },
            format = getLogFormat(config),
            spacing = getFormatSpacing(config),
            outputStream = process.stdout; // Set standard output stream

        // Write to specified file streams
        logToStream(stringifyLog(log, format, spacing), config);

        // Write request log to stdout if option is present
        config.logToConsole ? outputStream.write(stringifyLog(log, format, spacing) + getFormatSeparator(config)) : null;
    }
};

module.exports = customLogger;
<|endoftext|>