<commit_before>/*global $, jQuery */
/*jshint scripturl:true, smarttabs:true */

/**
 * Copyright 2013 Sean Coker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Taggle is a simple delicious style tagging plugin for jQuery
 *
 * @author Sean Coker <sean@seancoker.com>
 * @url http://seancoker.com/projects/tagglejs
 *
 */

(function($) {

	$.fn.taggle = function(options) {

		var defaults = {

			/**
			 * Class names to be added on each tag entered
			 * @type {String}
			 */
			additionalTagClasses:	'',

			/**
			 * Allow duplicate tags to be entered in the field?
			 * @type {Boolean}
			 */
			allowDuplicates:		false,

			/**
			 * Class name that will be added onto duplicate existant tag
			 * @type {String}
			 */
			duplicateTagClass:		'',

			/**
			 * Class added to the container div when focused
			 * @type {String}
			 */
			containerFocusClass:	'active',

			/**
			 * Name added to the hidden inputs within each tag
			 * @type {String}
			 */
			hiddenInputName:		'taggles[]',

			/**
			 * Tags that should be preloaded in the div on load
			 * @type {Array}
			 */
			tags:					null,

			/**
			 * Approximate font size (px) of the tags. Used to estimate where
			 * to break when typing
			 * @type {Number}
			 */
			fontSize:				11,

			/**
			 * If within a form, you can specify the tab index flow
			 * @type {Number}
			 */
			tabIndex:				1
		};

		var settings = $.extend({}, defaults, options);

		return this.each(function() {
			var container = $(this),

				the_tags = [],
				tag_list = document.createElement('ul'),
				tag_input_li = document.createElement('li'),
				tag_input = document.createElement('input');

			/**
			 * Fire off initializers
			 */
			function init() {
				setupEl();
				attachEvents();
			}

			/**
			 * Setup the div container for tags to be entered
			 * @return {[type]}
			 */
			function setupEl() {
				tag_list.className = 'taggle_list';
				tag_input.type = 'text';
				tag_input.className = 'taggle_input';
				tag_input.tabIndex = settings.tabIndex;

				if (settings.tags !== null) {
					for (var i = 0; i < settings.tags.length; i++) {
						tag_list.innerHTML += '
' +
						settings.tags[i] +
						'×
';
						the_tags.push(settings.tags[i].toLowerCase());
					}
				}

				tag_input_li.appendChild(tag_input);
				tag_list.appendChild(tag_input_li);
				container.append(tag_list);
			}

			/**
			 * Attaches events, duh
			 * @return {void}
			 */
			function attachEvents() {
				container.on('click', function() {
					tag_input.focus();
				});

				tag_input.onfocus = inputFocused;
				tag_input.onblur = inputBlurred;

				if (tag_input.addEventListener) {
					tag_input.addEventListener('keydown', keydownEvents, false);
					tag_input.addEventListener('keyup', keyupEvents, false);
				} else if (tag_input.attachEvent) {
					tag_input.attachEvent('onkeydown', keydownEvents);
					tag_input.attachEvent('onkeyup', keyupEvents);
				}

				container.find('.close').one('click', removeTag);
			}

			/**
			 * Resizes the hidden input where user types to fill in the
			 * width of the div
			 * @return {void}
			 */
			function fixInputWidth() {
				//reset width incase we've broken to the next line on a backspace erase
				setInputWidth();

				var width = ~~container.width(),
					left_pos = ~~container.find('.taggle_input').offset().left - ~~container.offset().left,
					padding = container.outerWidth() - width;

					setInputWidth(width - left_pos - padding);
			}

			/**
			 * Appends tag with its corresponding input to the list
			 * @param  {String} tag
			 * @return {void}
			 */
			function confirmTag(tag) {
				var li = document.createElement('li'),
					a = document.createElement('a'),
					hidden = document.createElement('input'),
					tag_input_value = tag ? tag.toLowerCase() : $.trim(tag_input.value.toLowerCase());

				a.href = 'javascript:void(0)';
				a.innerHTML = '×';
				a.className = 'close';
				a.onclick = removeTag;
				li.innerHTML = tag_input_value;
				li.className = 'taggle ' + settings.additionalTagClasses;
				hidden.type = 'hidden';
				hidden.value = tag_input_value;
				if (settings.hiddenInputName) {
					hidden.name = settings.hiddenInputName;
				}

				the_tags.push(tag_input_value);

				li.appendChild(a);
				li.appendChild(hidden);

				$(tag_list).find('li:last').before(li);
				$(li).find('.close:last').on('click', removeTag);

				tag_input.value = '';
				setInputWidth();
				fixInputWidth();
			}

			/**
			 * Activated when backspace key is pressed, removes last tag if
			 * it has already been probed
			 * @param  {Event}  e
			 * @return {void}
			 */
			function chargeLastTag(e) {
				e = e || window.event;

				var last_taggle = container.find('.taggle:last'),
					hot_class = 'hot';

				//8 - backspace
				if (tag_input.value === '' && e.keyCode === 8) {
					if (last_taggle.hasClass(hot_class)) {
						removeFromTheTags(last_taggle[0]);
						last_taggle.remove();
						fixInputWidth();
					}
					else {
						last_taggle.addClass(hot_class);
					}
				} else if (last_taggle.hasClass(hot_class)) {
					last_taggle.removeClass(hot_class);
				}
			}

			/**
			 * Grabs the text from the li item and removes it from global array
			 * @param  {Element} el
			 * @return {void}
			 */
			function removeFromTheTags(el) {
				var elm = el.tagName === 'A' ? el.parentNode : el;
					text = elm.textContent || elm.innerText;

				text = text.slice(0, -1);

				the_tags.splice(the_tags.indexOf(text), 1);
			}

			/**
			 * Setter for the hidden input.
			 * @param {Number} width
			 * @return {void}
			 */
			function setInputWidth(width) {
				tag_input.style.width = (width || 10) + 'px';
			}

			/**
			 * Checks global tags array if provided tag exists
			 * @param  {String} tag
			 * @return {void}
			 */
			function checkForDupes(tag) {
				var tag_input_value = tag ? tag.toLowerCase() : $.trim(tag_input.value.toLowerCase()),
					needle = $.inArray(tag_input_value.toLowerCase(), the_tags),
					taggle_list = container.find('.taggle_list');

				if (!!settings.duplicateTagClass && taggle_list.find('.' + settings.duplicateTagClass).length) {
					taggle_list.find('.' + settings.duplicateTagClass).removeClass(settings.duplicateTagClass);
				}

				//if found
				if (needle > -1) {
					taggle_list.children()[needle].className += ' ' + settings.duplicateTagClass;
					return true;
				}
				return false;
			}

			/**
			 * Checks whether or not the key pressed is acceptable
			 * @param  {Event}  e
			 * @return {Boolean}
			 */
			function isConfirmKey(e) {
				var code = e.keyCode,
					isConfirm = false;
				// comma or tab or enter
				if (code === 188 || code === 9 || code === 13) {
					isConfirm = true;
				}

				return isConfirm;
			}

			//event handlers

			/**
			 * Adds focus class to div container.
			 * @return {void}
			 */
			function inputFocused() {
				fixInputWidth();
				if (!container.hasClass(settings.containerFocusClass)) {
					container.addClass(settings.containerFocusClass);
				}
			}

			/**
			 * Sets state of container when blurred
			 * @return {void}
			 */
			function inputBlurred() {
				tag_input.value = '';
				setInputWidth();
				if (container.hasClass(settings.containerFocusClass)) {
					container.removeClass(settings.containerFocusClass);
				}
			}

			/**
			 * Runs all the events that need to run on keydown
			 * @param  {Event} e
			 * @return {void}
			 */
			function keydownEvents(e) {
				e = e || window.event;

				listenForEndOfContainer();
				// comma or tab or enter
				if (isConfirmKey(e) && tag_input.value !== '') {
					listenForTagConfirm(e);
				}
				//backspace
				if (e.keyCode === 8) {

					chargeLastTag(e);
				}
			}

			/**
			 * Runs all the events that need to run on keyup
			 * @param  {Event} e
			 * @return {void}
			 */
			function keyupEvents(e) {
				e = e || window.event;
			}

			/**
			 * Confirms the inputted value to be converted to a tag
			 * @param  {Event} e
			 * @return {Boolean}
			 */
			function listenForTagConfirm(e) {
				var code = e.keyCode,
					last_taggle = container.find('.taggle:last'),
					dupes;

				//tab, enter
				if (code === 9 || code === 13) {
					dupes = checkForDupes();
				}
				if (!dupes || settings.allowDuplicates) {
					confirmTag();
				}

				e.preventDefault();
				return false;

			}

			/**
			 * Approximates when the hidden input should break to the next line
			 * @return {void}
			 */
			function listenForEndOfContainer() {
				var apx_width = (settings.fontSize / 8) * 4.5 * tag_input.value.length, //32px wide input = 5chars = 11px font
					padding = container.outerWidth() - container.width(),
					max = container.outerWidth() - padding;

				if (apx_width + 5 > parseInt(tag_input.style.width, 10)) {
					tag_input.style.width = max + 'px';
				}
			}

			/**
			 * Removes tag from the list and global tags array
			 * @param  {Event} e
			 * @return {void}
			 */
			function removeTag(e) {
				e = e || window.event;
				var li = $(e.target).parent('li');

				li.remove();
				inputFocused();
				removeFromTheTags(e.target);
			}

			// Bang bang bang skeet skeet
			init();
		});
	};
})(jQuery);<commit_msg>typing after tag has been hot turns it back normal. fix ff bug<commit_after>/*global $, jQuery */
/*jshint scripturl:true, smarttabs:true */

/**
 * Copyright 2013 Sean Coker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Taggle is a simple delicious style tagging plugin for jQuery
 *
 * @author Sean Coker <sean@seancoker.com>
 * @url http://seancoker.com/projects/tagglejs
 *
 */

(function($) {

	$.fn.taggle = function(options) {

		var defaults = {

			/**
			 * Class names to be added on each tag entered
			 * @type {String}
			 */
			additionalTagClasses:	'',

			/**
			 * Allow duplicate tags to be entered in the field?
			 * @type {Boolean}
			 */
			allowDuplicates:		false,

			/**
			 * Class name that will be added onto duplicate existant tag
			 * @type {String}
			 */
			duplicateTagClass:		'',

			/**
			 * Class added to the container div when focused
			 * @type {String}
			 */
			containerFocusClass:	'active',

			/**
			 * Name added to the hidden inputs within each tag
			 * @type {String}
			 */
			hiddenInputName:		'taggles[]',

			/**
			 * Tags that should be preloaded in the div on load
			 * @type {Array}
			 */
			tags:					null,

			/**
			 * Approximate font size (px) of the tags. Used to estimate where
			 * to break when typing
			 * @type {Number}
			 */
			fontSize:				11,

			/**
			 * If within a form, you can specify the tab index flow
			 * @type {Number}
			 */
			tabIndex:				1
		};

		var settings = $.extend({}, defaults, options);

		return this.each(function() {
			var container = $(this),

				the_tags = [],
				tag_list = document.createElement('ul'),
				tag_input_li = document.createElement('li'),
				tag_input = document.createElement('input');

			/**
			 * Fire off initializers
			 */
			function init() {
				setupEl();
				attachEvents();
			}

			/**
			 * Setup the div container for tags to be entered
			 * @return {[type]}
			 */
			function setupEl() {
				tag_list.className = 'taggle_list';
				tag_input.type = 'text';
				tag_input.className = 'taggle_input';
				tag_input.tabIndex = settings.tabIndex;

				if (settings.tags !== null) {
					for (var i = 0; i < settings.tags.length; i++) {
						tag_list.innerHTML += '
' +
						settings.tags[i] +
						'×' +
						'<input type="hidden" value="' +
						settings.tags[i] + '" name="' +
						settings.hiddenInputName +'">
';
						the_tags.push(settings.tags[i].toLowerCase());
					}
				}

				tag_input_li.appendChild(tag_input);
				tag_list.appendChild(tag_input_li);
				container.append(tag_list);
			}

			/**
			 * Attaches events, duh
			 * @return {void}
			 */
			function attachEvents() {
				container.on('click', function() {
					tag_input.focus();
				});

				tag_input.onfocus = inputFocused;
				tag_input.onblur = inputBlurred;

				if (tag_input.addEventListener) {
					tag_input.addEventListener('keydown', keydownEvents, false);
					tag_input.addEventListener('keyup', keyupEvents, false);
				} else if (tag_input.attachEvent) {
					tag_input.attachEvent('onkeydown', keydownEvents);
					tag_input.attachEvent('onkeyup', keyupEvents);
				}

				container.find('.close').one('click', removeTag);
			}

			/**
			 * Resizes the hidden input where user types to fill in the
			 * width of the div
			 * @return {void}
			 */
			function fixInputWidth() {
				//reset width incase we've broken to the next line on a backspace erase
				setInputWidth();

				var width = ~~container.width(),
					left_pos = ~~container.find('.taggle_input').offset().left - ~~container.offset().left,
					padding = container.outerWidth() - width;

					setInputWidth(width - left_pos - padding);
			}

			/**
			 * Appends tag with its corresponding input to the list
			 * @param  {String} tag
			 * @return {void}
			 */
			function confirmTag(tag) {
				var li = document.createElement('li'),
					a = document.createElement('a'),
					hidden = document.createElement('input'),
					tag_input_value = tag ? tag.toLowerCase() : $.trim(tag_input.value.toLowerCase());

				a.href = 'javascript:void(0)';
				a.innerHTML = '×';
				a.className = 'close';
				a.onclick = removeTag;
				li.innerHTML = tag_input_value;
				li.className = 'taggle ' + settings.additionalTagClasses;
				hidden.type = 'hidden';
				hidden.value = tag_input_value;
				if (settings.hiddenInputName) {
					hidden.name = settings.hiddenInputName;
				}

				the_tags.push(tag_input_value);

				li.appendChild(a);
				li.appendChild(hidden);

				$(tag_list).find('li:last').before(li);
				$(li).find('.close:last').on('click', removeTag);

				tag_input.value = '';
				setInputWidth();
				fixInputWidth();
			}

			/**
			 * Removes last tag if it has already been probed
			 * @param  {Event}  e
			 * @return {void}
			 */
			function chargeLastTag(e) {
				e = e || window.event;

				var last_taggle = container.find('.taggle:last'),
					hot_class = 'hot';

				//8 - backspace
				if (tag_input.value === '' && e.keyCode === 8) {
					if (last_taggle.hasClass(hot_class)) {
						removeFromTheTags(last_taggle[0]);
						last_taggle.remove();
						fixInputWidth();
					}
					else {
						last_taggle.addClass(hot_class);
					}
				} else if (last_taggle.hasClass(hot_class)) {
					last_taggle.removeClass(hot_class);
				}
			}

			/**
			 * Grabs the text from the li item and removes it from global array
			 * @param  {Element} el
			 * @return {void}
			 */
			function removeFromTheTags(el) {
				var elm = el.tagName === 'A' ? el.parentNode : el;
					text = elm.textContent || elm.innerText;

				text = text.slice(0, -1);

				the_tags.splice(the_tags.indexOf(text), 1);
			}

			/**
			 * Setter for the hidden input.
			 * @param {Number} width
			 * @return {void}
			 */
			function setInputWidth(width) {
				tag_input.style.width = (width || 10) + 'px';
			}

			/**
			 * Checks global tags array if provided tag exists
			 * @param  {String} tag
			 * @return {void}
			 */
			function checkForDupes(tag) {
				var tag_input_value = tag ? tag.toLowerCase() : $.trim(tag_input.value.toLowerCase()),
					needle = $.inArray(tag_input_value.toLowerCase(), the_tags),
					taggle_list = container.find('.taggle_list');

				if (!!settings.duplicateTagClass && taggle_list.find('.' + settings.duplicateTagClass).length) {
					taggle_list.find('.' + settings.duplicateTagClass).removeClass(settings.duplicateTagClass);
				}

				//if found
				if (needle > -1) {
					taggle_list.children()[needle].className += ' ' + settings.duplicateTagClass;
					return true;
				}
				return false;
			}

			/**
			 * Checks whether or not the key pressed is acceptable
			 * @param  {Event}  e
			 * @return {Boolean}
			 */
			function isConfirmKey(e) {
				var code = e.keyCode,
					isConfirm = false;
				// comma or tab or enter
				if (code === 188 || code === 9 || code === 13) {
					isConfirm = true;
				}

				return isConfirm;
			}

			//event handlers

			/**
			 * Adds focus class to div container.
			 * @return {void}
			 */
			function inputFocused() {
				fixInputWidth();
				if (!container.hasClass(settings.containerFocusClass)) {
					container.addClass(settings.containerFocusClass);
				}
			}

			/**
			 * Sets state of container when blurred
			 * @return {void}
			 */
			function inputBlurred() {
				tag_input.value = '';
				setInputWidth();
				if (container.hasClass(settings.containerFocusClass)) {
					container.removeClass(settings.containerFocusClass);
				}
			}

			/**
			 * Runs all the events that need to run on keydown
			 * @param  {Event} e
			 * @return {void}
			 */
			function keydownEvents(e) {
				e = e || window.event;

				listenForEndOfContainer();
				// comma or tab or enter
				if (isConfirmKey(e) && tag_input.value !== '') {
					listenForTagConfirm(e);
				}
				chargeLastTag(e);
			}

			/**
			 * Runs all the events that need to run on keyup
			 * @param  {Event} e
			 * @return {void}
			 */
			function keyupEvents(e) {
				e = e || window.event;
			}

			/**
			 * Confirms the inputted value to be converted to a tag
			 * @param  {Event} e
			 * @return {Boolean}
			 */
			function listenForTagConfirm(e) {
				e = e || window.event;

				var code = e.keyCode,
					last_taggle = container.find('.taggle:last'),
					dupes;

				//tab, enter
				if (code === 9 || code === 13) {
					dupes = checkForDupes();
				}
				if (!dupes || settings.allowDuplicates) {
					confirmTag();
				}
				e.preventDefault();
				return false;

			}

			/**
			 * Approximates when the hidden input should break to the next line
			 * @return {void}
			 */
			function listenForEndOfContainer() {
				var apx_width = (settings.fontSize / 8) * 4.5 * tag_input.value.length, //32px wide input = 5chars = 11px font
					padding = container.outerWidth() - container.width(),
					max = container.outerWidth() - padding;

				if (apx_width + 5 > parseInt(tag_input.style.width, 10)) {
					tag_input.style.width = max + 'px';
				}
			}

			/**
			 * Removes tag from the list and global tags array
			 * @param  {Event} e
			 * @return {void}
			 */
			function removeTag(e) {
				e = e || window.event;
				var li = $(e.target).parent('li');

				li.remove();
				inputFocused();
				removeFromTheTags(e.target);
			}

			// Bang bang bang skeet skeet
			init();
		});
	};
})(jQuery);<|endoftext|>


<commit_before><commit_msg>adds logic to prefer new IPEDS cubes<commit_after><|endoftext|>


<commit_before>'use strict';

import { async } from 'metal';
import { Align } from 'metal-position';
import Autocomplete from '../src/Autocomplete';
import UA from 'metal-useragent';
import dom from 'metal-dom';

var component;
var input;

var filterData = function(query) {
	return ['Alabama', 'Alaska'].filter(function(item) {
		return item.toLowerCase().indexOf(query.toLowerCase()) === 0;
	});
};

var simulateFocus = function(element) {
	element.focus();

	if (UA.isFirefox) {
		dom.triggerEvent(element, 'focus');
	}
};

describe('Autocomplete', function() {
	afterEach(function() {
		if (component) {
			component.dispose();
		}
		if (input) {
			dom.exitDocument(input);
		}
	});

	beforeEach(function() {
		input = document.createElement('input');
		input.type = 'text';
		dom.enterDocument(input);
	});

	it('should process valid query and display element', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		component.request('a').then(function() {
			async.nextTick(function() {
				assert.ok(component.visible);
				assert.strictEqual(2, component.element.querySelectorAll('li').length);
				done();
			});
		});
	});

	it('should process invalid query and hide element', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		component.request('asparagus').then(function() {
			async.nextTick(function() {
				assert.ok(!component.visible);
				assert.strictEqual(0, component.element.querySelectorAll('li').length);
				done();
			});
		});
	});

	it('should process query null data and hide element', function(done) {
		component = new Autocomplete({
			data: null,
			inputElement: input
		});

		component.request('asparagus').then(function() {
			async.nextTick(function() {
				assert.ok(!component.visible);
				assert.strictEqual(0, component.element.querySelectorAll('li').length);
				done();
			});
		});
	});

	it('should throws error with malformed data structure', function(done) {
		component = new Autocomplete({
			data: [1],
			inputElement: input
		});

		component.request('query').catch(function(reason) {
			assert.strictEqual('Autocomplete item must be an object', reason.message);
			done();
		});
	});

	it('should throws error with malformed data object structure', function(done) {
		component = new Autocomplete({
			data: [{
				foo: 'foo'
			}],
			inputElement: input
		});

		component.request('query').catch(function(reason) {
			assert.strictEqual('Autocomplete item must be an object with \'textPrimary\' key', reason.message);
			done();
		});
	});

	it('should close dropdown list when item is selected', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.setAttribute('value', 'a');
		dom.triggerEvent(input, 'input');
		async.nextTick(function() {
			async.nextTick(function() {
				component.once('select', function(value) {
					assert.strictEqual('Alabama', value.text);
					async.nextTick(function() {
						assert.ok(!component.visible);
						done();
					});
				});
				dom.triggerEvent(component.element.querySelectorAll('li')[0], 'click');
			});
		});
	});

	it('should select an option by pressing enter key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				component.once('select', function(value) {
					assert.strictEqual('Alabama', value.text);
					done();
				});
				dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 13});
			});
		});
	});

	it('should select an option by pressing space key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				component.once('select', function(value) {
					assert.strictEqual('Alabama', value.text);
					done();
				});
				dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 32});
			});
		});
	});

	it('should hide element when click outside input', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		var otherInput = document.createElement('input');
		otherInput.type = 'text';
		dom.enterDocument(otherInput);

		input.setAttribute('value', 'a');
		simulateFocus(input);
		async.nextTick(function() {
			async.nextTick(function() {
				async.nextTick(function() {
					assert.ok(!component.visible);
					dom.exitDocument(otherInput);
					done();
				});
				assert.ok(component.visible);
				otherInput.focus();
				dom.triggerEvent(otherInput, 'click');
			});
		});
	});

	it('should not hide element when clicking inside input', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.setAttribute('value', 'a');
		simulateFocus(input);
		async.nextTick(function() {
			async.nextTick(function() {
				assert.ok(component.visible);
				done();
			});
			assert.ok(component.visible);
			dom.triggerEvent(input, 'click');
		});
	});

	it('should show element when focus input', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input,
			visible: false
		});

		input.value = 'Alabama';
		simulateFocus(input);

		async.nextTick(function() {
			assert.ok(component.visible);
			done();
		});
	});

	it('should link the input with the list by aria-owns attribute', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		async.nextTick(function() {
			let listComponentElement = component.getList().element;
			assert.strictEqual(input.getAttribute('aria-owns'), listComponentElement.querySelector('.list-group').getAttribute('id'));
			done();
		});
	});

	it('should active the first item as soon as the list appears', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				let listComponentElement = component.getList().element;
				assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));
				done();
			});
		});
	});

	it('should navigate to the next option by pressing down arrow key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				let listComponentElement = component.getList().element;
				assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));

				async.nextTick(function() {
					dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});
					assert.notOk(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));
					assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(2)'), 'active'));
					done();
				});
			});
		});
	});

	it('should navigate to the last option by pressing up arrow key if the first one is selected', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				let listComponentElement = component.getList().element;
				assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));

				async.nextTick(function() {
					dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 38});

					let listComponentElement = component.getList().element;
					assert.notOk(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));
					assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:last-child'), 'active'));
					done();
				});
			});
		});
	});

	it('should navigate to the first option by pressing down arrow key if the last one is selected', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				let listComponentElement = component.getList().element;
				assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));

				async.nextTick(function() {
					dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});
					dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});
					assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));
					assert.notOk(dom.hasClass(listComponentElement.querySelector('.listitem:last-child'), 'active'));
					done();
				});
			});
		});
	});

	it('should navigate to the previous option by pressing up arrow key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				let listComponentElement = component.getList().element;
				dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});
				assert.notOk(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));
				assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(2)'), 'active'));

				async.nextTick(function() {
					dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 38});

					assert.ok(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(1)'), 'active'));
					assert.notOk(dom.hasClass(listComponentElement.querySelector('.listitem:nth-child(2)'), 'active'));
					done();
				});
			});
		});
	});

	describe('Align', function() {
		beforeEach(function() {
			sinon.stub(Align, 'align');
		});

		afterEach(function() {
			Align.align.restore();
		});

		it('should update width to be equal to the input\'s width', function() {
			input.style.width = '200px';
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});
			assert.strictEqual(input.offsetWidth, component.element.offsetWidth);
		});

		it('should update width to be equal to the input\'s width when window resizes', function(done) {
			input.style.width = '200px';
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			// Simulating use case where resizing the window causes input width to change.
			input.style.width = '400px';
			dom.triggerEvent(window, 'resize');

			// Waits for the resize event's debounce function to finish.
			setTimeout(function() {
				assert.strictEqual(input.offsetWidth, component.element.offsetWidth);
				done();
			}, 200);
		});

		it('should align element when it is created already visible', function() {
			sinon.spy(Autocomplete.prototype, 'attached');
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});
			assert.ok(Align.align.calledAfter(component.attached));
			Autocomplete.prototype.attached.restore();
		});

		it('should align element when it becomes visible', function(done) {
			component = new Autocomplete({
				data: filterData,
				inputElement: input
			});
			assert.strictEqual(0, Align.align.callCount);

			component.visible = true;
			component.once('stateSynced', function() {
				assert.strictEqual(1, Align.align.callCount);
				done();
			});
		});

		it('should realign element when window resizes while the results are visible', function(done) {
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			Align.align.restore();
			sinon.spy(Align, 'align');
			dom.triggerEvent(window, 'resize');

			// Waits for the resize event's debounce function to finish.
			setTimeout(function() {
				assert.strictEqual(1, Align.align.callCount);
				done();
			}, 200);
		});

		it('should not realign element when window resizes while the results aren\'t visible', function(done) {
			component = new Autocomplete({
				data: filterData,
				inputElement: input
			});

			dom.triggerEvent(window, 'resize');

			// Waits for the resize event's debounce function to finish.
			setTimeout(function() {
				assert.strictEqual(0, Align.align.callCount);
				done();
			}, 200);
		});

		it('should add "autocomplete-bottom" css class if results are aligned on the bottom', function() {
			Align.align.returns(Align.Bottom);
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			assert.ok(dom.hasClass(component.element, 'autocomplete-bottom'));
		});

		it('should add "autocomplete-top" css class if results are aligned on the top', function() {
			Align.align.returns(Align.Top);
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			assert.ok(dom.hasClass(component.element, 'autocomplete-top'));
		});
	});
});
<commit_msg>Makes more understandable tests<commit_after>'use strict';

import { async } from 'metal';
import { Align } from 'metal-position';
import Autocomplete from '../src/Autocomplete';
import UA from 'metal-useragent';
import dom from 'metal-dom';

var component;
var input;

var filterData = function(query) {
	return ['Alabama', 'Alaska'].filter(function(item) {
		return item.toLowerCase().indexOf(query.toLowerCase()) === 0;
	});
};

var simulateFocus = function(element) {
	element.focus();

	if (UA.isFirefox) {
		dom.triggerEvent(element, 'focus');
	}
};

var getListItem = function(index) {
	return getListItems().item(index);
};

var getLastListItem = function() {
	let list = getListItems();
	return getListItem(list.length - 1);
};

var getListItems = function() {
	return component.getList().element.querySelectorAll('.listitem');
};

describe('Autocomplete', function() {
	afterEach(function() {
		if (component) {
			component.dispose();
		}
		if (input) {
			dom.exitDocument(input);
		}
	});

	beforeEach(function() {
		input = document.createElement('input');
		input.type = 'text';
		dom.enterDocument(input);
	});

	it('should process valid query and display element', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		component.request('a').then(function() {
			async.nextTick(function() {
				assert.ok(component.visible);
				assert.strictEqual(2, component.element.querySelectorAll('li').length);
				done();
			});
		});
	});

	it('should process invalid query and hide element', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		component.request('asparagus').then(function() {
			async.nextTick(function() {
				assert.ok(!component.visible);
				assert.strictEqual(0, component.element.querySelectorAll('li').length);
				done();
			});
		});
	});

	it('should process query null data and hide element', function(done) {
		component = new Autocomplete({
			data: null,
			inputElement: input
		});

		component.request('asparagus').then(function() {
			async.nextTick(function() {
				assert.ok(!component.visible);
				assert.strictEqual(0, component.element.querySelectorAll('li').length);
				done();
			});
		});
	});

	it('should throws error with malformed data structure', function(done) {
		component = new Autocomplete({
			data: [1],
			inputElement: input
		});

		component.request('query').catch(function(reason) {
			assert.strictEqual('Autocomplete item must be an object', reason.message);
			done();
		});
	});

	it('should throws error with malformed data object structure', function(done) {
		component = new Autocomplete({
			data: [{
				foo: 'foo'
			}],
			inputElement: input
		});

		component.request('query').catch(function(reason) {
			assert.strictEqual('Autocomplete item must be an object with \'textPrimary\' key', reason.message);
			done();
		});
	});

	it('should close dropdown list when item is selected', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.setAttribute('value', 'a');
		dom.triggerEvent(input, 'input');
		async.nextTick(function() {
			async.nextTick(function() {
				component.once('select', function(value) {
					assert.strictEqual('Alabama', value.text);
					async.nextTick(function() {
						assert.ok(!component.visible);
						done();
					});
				});
				dom.triggerEvent(component.element.querySelectorAll('li')[0], 'click');
			});
		});
	});

	it('should select an option by pressing enter key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				component.once('select', function(value) {
					assert.strictEqual('Alabama', value.text);
					done();
				});
				dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 13});
			});
		});
	});

	it('should select an option by pressing space key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		async.nextTick(function() {
			async.nextTick(function() {
				component.once('select', function(value) {
					assert.strictEqual('Alabama', value.text);
					done();
				});
				dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 32});
			});
		});
	});

	it('should hide element when click outside input', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		var otherInput = document.createElement('input');
		otherInput.type = 'text';
		dom.enterDocument(otherInput);

		input.setAttribute('value', 'a');
		simulateFocus(input);
		async.nextTick(function() {
			async.nextTick(function() {
				async.nextTick(function() {
					assert.ok(!component.visible);
					dom.exitDocument(otherInput);
					done();
				});
				assert.ok(component.visible);
				otherInput.focus();
				dom.triggerEvent(otherInput, 'click');
			});
		});
	});

	it('should not hide element when clicking inside input', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.setAttribute('value', 'a');
		simulateFocus(input);
		async.nextTick(function() {
			async.nextTick(function() {
				assert.ok(component.visible);
				done();
			});
			assert.ok(component.visible);
			dom.triggerEvent(input, 'click');
		});
	});

	it('should show element when focus input', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input,
			visible: false
		});

		input.value = 'Alabama';
		simulateFocus(input);

		async.nextTick(function() {
			assert.ok(component.visible);
			done();
		});
	});

	it('should link the input with the list by aria-owns attribute', function() {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		let listComponentElement = component.getList().element;
		assert.strictEqual(input.getAttribute('aria-owns'), listComponentElement.querySelector('.list-group').getAttribute('id'));
	});

	it('should active the first item as soon as the list appears', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		component.on('stateSynced', function() {
			assert.ok(dom.hasClass(getListItem(0), 'active'));
			done();
		});
	});

	it('should navigate to the next option by pressing down arrow key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		component.on('stateSynced', function() {
			assert.ok(dom.hasClass(getListItem(0), 'active'));

			dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});
			assert.notOk(dom.hasClass(getListItem(0), 'active'));
			assert.ok(dom.hasClass(getListItem(1), 'active'));
			done();
		});
	});

	it('should navigate to the last option by pressing up arrow key if the first one is selected', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';

		component.on('stateSynced', function() {
			assert.ok(dom.hasClass(getListItem(0), 'active'));

			dom.triggerEvent(input, 'keydown', {keyCode: 38});
			assert.notOk(dom.hasClass(getListItem(0), 'active'));
			assert.ok(dom.hasClass(getLastListItem(), 'active'));
			done();
		});

		simulateFocus(input);
	});

	it('should navigate to the first option by pressing down arrow key if the last one is selected', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		component.on('stateSynced', function() {
			assert.ok(dom.hasClass(getListItem(0), 'active'));

			dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});
			dom.triggerEvent(component.inputElement, 'keydown', {keyCode: 40});

			assert.ok(dom.hasClass(getListItem(0), 'active'));
			assert.notOk(dom.hasClass(getListItems(1), 'active'));
			done();
		});
	});

	it('should navigate to the previous option by pressing up arrow key', function(done) {
		component = new Autocomplete({
			data: filterData,
			inputElement: input
		});

		input.value = 'Al';
		simulateFocus(input);

		component.on('stateSynced', function() {
			dom.triggerEvent(input, 'keydown', {keyCode: 40});

			assert.notOk(dom.hasClass(getListItem(0), 'active'));
			assert.ok(dom.hasClass(getListItem(1), 'active'));

			dom.triggerEvent(input, 'keydown', {keyCode: 38});

			assert.ok(dom.hasClass(getListItem(0), 'active'));
			assert.notOk(dom.hasClass(getListItem(1), 'active'));
			done();
		});
	});

	describe('Align', function() {
		beforeEach(function() {
			sinon.stub(Align, 'align');
		});

		afterEach(function() {
			Align.align.restore();
		});

		it('should update width to be equal to the input\'s width', function() {
			input.style.width = '200px';
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});
			assert.strictEqual(input.offsetWidth, component.element.offsetWidth);
		});

		it('should update width to be equal to the input\'s width when window resizes', function(done) {
			input.style.width = '200px';
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			// Simulating use case where resizing the window causes input width to change.
			input.style.width = '400px';
			dom.triggerEvent(window, 'resize');

			// Waits for the resize event's debounce function to finish.
			setTimeout(function() {
				assert.strictEqual(input.offsetWidth, component.element.offsetWidth);
				done();
			}, 200);
		});

		it('should align element when it is created already visible', function() {
			sinon.spy(Autocomplete.prototype, 'attached');
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});
			assert.ok(Align.align.calledAfter(component.attached));
			Autocomplete.prototype.attached.restore();
		});

		it('should align element when it becomes visible', function(done) {
			component = new Autocomplete({
				data: filterData,
				inputElement: input
			});
			assert.strictEqual(0, Align.align.callCount);

			component.visible = true;
			component.once('stateSynced', function() {
				assert.strictEqual(1, Align.align.callCount);
				done();
			});
		});

		it('should realign element when window resizes while the results are visible', function(done) {
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			Align.align.restore();
			sinon.spy(Align, 'align');
			dom.triggerEvent(window, 'resize');

			// Waits for the resize event's debounce function to finish.
			setTimeout(function() {
				assert.strictEqual(1, Align.align.callCount);
				done();
			}, 200);
		});

		it('should not realign element when window resizes while the results aren\'t visible', function(done) {
			component = new Autocomplete({
				data: filterData,
				inputElement: input
			});

			dom.triggerEvent(window, 'resize');

			// Waits for the resize event's debounce function to finish.
			setTimeout(function() {
				assert.strictEqual(0, Align.align.callCount);
				done();
			}, 200);
		});

		it('should add "autocomplete-bottom" css class if results are aligned on the bottom', function() {
			Align.align.returns(Align.Bottom);
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			assert.ok(dom.hasClass(component.element, 'autocomplete-bottom'));
		});

		it('should add "autocomplete-top" css class if results are aligned on the top', function() {
			Align.align.returns(Align.Top);
			component = new Autocomplete({
				data: filterData,
				inputElement: input,
				visible: true
			});

			assert.ok(dom.hasClass(component.element, 'autocomplete-top'));
		});
	});
});
<|endoftext|>


<commit_before>var chai = require('chai'),
    should = chai.should(),
    expect = chai.expect,
    _ = require('underscore'),
    testRunner = require('..').TestRunner.setup();

var C = {};

describe('js.core.Bindable', function () {

    before(function (done) {
        testRunner.requireClasses({
            Bindable: 'js/core/Bindable',
            List: 'js/core/List'
        }, C, done);

    });

    describe('#defaults', function () {

        var BindableWithDefaults;

        before(function () {
            BindableWithDefaults = C.Bindable.inherit({
                defaults: {
                    foo: "bar",
                    x: 1,
                    list: C.List
                }
            });
        });

        it('new instance should have default attributes without modification of defaults', function () {
            var b = new BindableWithDefaults();
            expect(b).to.exist;
            expect(b.$.foo).to.exist.and.to.be.equal('bar');
            expect(b.$.x).to.exist.and.to.be.equal(1);
            expect(b.$.list).to.an.instanceof(C.List);

        });

        it('attributes set, should not be overwritten by defaults', function () {
            var b = new BindableWithDefaults({
                foo: 'foo'
            });

            expect(b).to.exist;
            expect(b.$.foo).to.exist.and.to.be.equal('foo');
            expect(b.$.x).to.exist.and.to.be.equal(1);
            expect(b.$.list).to.an.instanceof(C.List);

        });

        it('defaults should not be modified', function () {

            expect(BindableWithDefaults.prototype.defaults.foo).to.exist.and.to.be.equal('bar');
            expect(BindableWithDefaults.prototype.defaults.x).to.exist.and.to.be.equal(1);
            expect(BindableWithDefaults.prototype.defaults.list).to.exist.and.to.be.equal(C.List);
        })

    });

    describe('get', function () {

        var b,
            undefined;

        beforeEach(function () {
            b = new C.Bindable();
        });

        it('#empty get', function () {
            should.not.exist(b.get());
        });

        it('#one path $', function () {
            b.$.foo = "bar";
            var val = b.get('foo');
            should.exist(val);
            val.should.eql('bar');
        });

        it('#one path json', function () {
            var input = {
                foo: "bar"
            };

            var val = b.get(input, 'foo');
            should.exist(val);
            val.should.eql('bar');
        });

        it('#long path exists', function () {
            var input = {
                foo: {
                    bar: "zoo"
                }
            };

            var val = b.get(input, 'foo.bar');
            should.exist(val);
            val.should.eql('zoo');
        });

        it('#long path not exists', function () {
            var input = {
                foo: {
                    bar: "zoo"
                }
            };

            var val = b.get(input, 'foo.zoo');
            should.not.exist(val);
        });

        it('#fnc value foo.bar()', function () {
            var input = {
                foo: {
                    bar: function () {
                        return "hello"
                    }
                }
            };
            var val = b.get(input, 'foo.bar()');
            val.should.eql('hello');
        });

        it('#array ', function () {
            b.$.foo = [1, 2, 3];

            var val = b.get('foo[1]');
            should.exist(val);
            val.should.eql(2);
        });

        it('#array to self', function () {

            var val = b.get([0, 1, 2], '[1]');
            should.exist(val);
            val.should.eql(1);
        });

        it('#return array', function () {

            var a = b.$.foo = [1, 2, 3];

            var val = b.get('foo');
            should.exist(val);
            a.should.eql(val);
        });

        it('#return List', function () {

            var a = b.$.foo = new C.List([1, 2, 3]);

            var val = b.get('foo');
            should.exist(val);
            a.should.eql(val);
        });

        it('#List ', function () {
            b.$.foo = new C.List([3, 4, 5, 6]);

            var val = b.get('foo[2]');
            should.exist(val);
            val.should.eql(5);
        });

        it('#nested array ', function () {
            b.$.foo = [
                [0, 1, 2],
                [3, 4, 5],
                [6, 7, 8]
            ];

            var val = b.get('foo[1].[2]');
            should.exist(val);
            val.should.eql(5);
        });

        it('#not existing array path', function () {
            b.$.foo = [];

            var val = b.get('foo[1].bar');
            should.not.exist(val);
        });

    });

    describe('bind', function(){

        var bindable;

        it('#should bind to event and trigger callback on scope', function(done){

            bindable = new C.Bindable({
                foo: "bar"
            });

            var scope = {};

            bindable.bind('change', function(){

                expect(scope).to.equal(this);

                done();

            }, scope);

            bindable.set('foo',"newValue");
        });

        it('#should bind to event behind a path', function(done){

            bindable = new C.Bindable({
                nested: new C.Bindable({
                    foo: "bar"
                })
            });

            var scope = {};

            bindable.bind('nested','change', function(){

                expect(scope).to.equal(this);
                done();

            },scope);

            bindable.$.nested.set('foo', "newValue");
        });

        it('#should bind to an event with a function name', function(done){

            var bindable = new C.Bindable({
                foo: "bar"
            });

            var scope = {
                callback: function(){
                    expect(this).to.equal(scope);
                    done();
                }
            };

            bindable.bind('change',"callback()", scope);

            bindable.set('foo', "newValue");

        });

        it('#should bind to an event with a function name and parameters', function(done){
            var bindable = new C.Bindable({
                foo: "bar",
                foo2: "bar2"
            });

            var scope = {
                callback: function (event, foo, foo2, n, b, s) {
                    expect(this).to.equal(scope);
                    expect(event).to.exist;
                    expect(foo).to.equal(bindable.$.foo);
                    expect(foo2).to.equal(bindable.$.foo2);
                    expect(n).to.equal(5);
                    expect(b).to.equal(true);
                    expect(s).to.equal('test');
                    done();
                }
            };

            bindable.bind('change', "callback(event,foo,foo2,5,true,'test')", scope);
            bindable.set('foo', "newValue");
        });

    });

    describe('clone', function () {
        var original, nestedBindable, copy;
        beforeEach(function () {
            nestedBindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
            original = new C.Bindable({
                str: 'String',
                number: 123,
                boolean: true,
                person: {
                    firstName: 'Peter',
                    lastName: 'Pan'
                },
                nested: nestedBindable,
                arr: [
                    nestedBindable, "ab", 2, "waht", ["a", "b"]
                ]
            });

            copy = original.clone();
        });

        it('#should clone all attributes', function () {
            for (var key in original.$) {
                if (original.$.hasOwnProperty(key)) {
                    expect(copy.$[key]).to.exist;
                }
            }
        });

        it('#should create a deep copy of each nested Object', function () {
            expect(copy.$.person).to.exist.and.not.to.be.equal(original.$.person);
            expect(copy.$.person.firstName).to.exist.and.to.be.equal(original.$.person.firstName);
            expect(copy.$.person.lastName).to.exist.and.to.be.equal(original.$.person.lastName);

            original.$.person.firstName = "Hans";
            expect(copy.$.person.firstName).not.be.equal(original.$.person.firstName);
        });

        it('#should create a deep copy of each nested Array', function () {
            var val, copyVal;
            expect(copy.$.arr).to.exist;
            expect(copy.$.arr.length).to.be.equal(original.$.arr.length);

            for (var i = 0; i < original.$.arr.length; i++) {
                val = original.$.arr[i];
                copyVal = copy.$.arr[i];
                expect(copyVal).to.exist;
                if (val instanceof C.Bindable) {
                    expect(val).not.to.be.equal(copyVal);
                } else if (_.isArray(val)) {
                    expect(val.length).to.be.equal(copyVal.length);
                    expect(val).not.to.be.equal(copyVal);
                } else if (_.isObject(val)) {
                    expect(val).not.to.be.equal(copyVal);
                } else {
                    // numbers, strings, boolean and stuff
                    expect(val).to.be.equal(copyVal);
                }
            }
        });

        it('#should clone all nested Bindables', function () {
            expect(copy.$.nested).to.exist.and.not.to.be.equal(original.$.nested);
            expect(copy.$.nested.$.street).to.exist.and.to.be.equal(original.$.nested.$.street);
            expect(copy.$.nested.$.city).to.exist.and.to.be.equal(original.$.nested.$.city);
        });
    });

    describe('sync', function () {
        var original, nestedBindable, copy;
        beforeEach(function () {
            nestedBindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
            original = new C.Bindable({
                str: 'String',
                number: 123,
                boolean: true,
                person: {
                    firstName: 'Peter',
                    lastName: 'Pan'
                },
                nested: nestedBindable,
                arr: [
                    nestedBindable, "ab", 2, "waht", ["a", "b"]
                ]
            });

            copy = original.clone();
        });

        it('#should remove unset attributes from source', function () {
            copy.set('person',null,{unset: true});
            copy.set('nested', null, {unset: true});

            copy.sync();

            expect(original.$.person).not.to.exist;
            expect(original.$.nested).not.to.exist;
        });

        it('#should replace nested Bindable if they are new', function(){
            // create new Bindable
            copy.set('nested',new C.Bindable({
                street : 'A',
                city: 'B'
            }));

            copy.sync();

            expect(original.$.nested).not.to.be.equal(nestedBindable);
            expect(original.$.nested.$.street).to.be.equal(copy.$.nested.$.street);
            expect(original.$.nested.$.city).to.be.equal(copy.$.nested.$.city);

        });

        it('#should not replace nested Bindable if they were modified', function () {
            // modify nested Bindable
            copy.$.nested.set({
                street: 'A',
                city: 'B'
            });

            copy.sync();

            expect(original.$.nested).to.be.equal(nestedBindable);
            expect(original.$.nested.$.street).to.be.equal(copy.$.nested.$.street);
            expect(original.$.nested.$.city).to.be.equal(copy.$.nested.$.city);
        });
    });

    describe('#isDeepEqual', function(){
        var original, nestedBindable, copy;
        beforeEach(function () {
            nestedBindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
            original = new C.Bindable({
                str: 'String',
                number: 123,
                boolean: true,
                person: {
                    firstName: 'Peter',
                    lastName: 'Pan'
                },
                nested: nestedBindable,
                arr: [
                    nestedBindable, "ab", 2, "what", ["a", "b"]
                ]
            });

            copy = original.clone();
        });

        it('#exact clone should be equal', function () {
            expect(original.isDeepEqual(copy)).to.equal(true);
        });

        it('#modified clone with same values should be equal', function(){
            copy.set("arr", [
                nestedBindable.clone(),
                "ab",
                2,
                "what",
                ["a", "b"]
            ]);
            expect(original.isDeepEqual(copy)).to.equal(true);
        });

        it('#modified clone should not equal', function(){
            copy.set('number',312);
            expect(original.isDeepEqual(copy)).to.equal(false);
        });
    });

    describe('#destroy', function(){

        var bindable;

        beforeEach(function () {
            bindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
        });

        it('should trigger destroy event', function(done){
            var destroyed = false;
            bindable.bind('destroy', function(){
                destroyed = true;
                done();
            });

            bindable.destroy();
            expect(destroyed).to.equal(true);
        });

        it('should remove all event listeners', function(){

            var triggered = false;

            bindable.bind('change', function(){
                triggered = true;
            });

            bindable.destroy();

            bindable.set('street','New Street');

            expect(triggered).to.equal(false);
        });

        it('should destroy all event bindables', function(){

            bindable.set('person', new C.Bindable({
                name: "Max"
            }));

            var triggered = 0;

            bindable.bind('person','change', function(){
                triggered++;
            });

            bindable.$.person.set('name','Peter');

            expect(triggered).to.be.equal(1);

            bindable.destroy();

            bindable.$.person.set('name','Max');

            expect(triggered).to.be.equal(1);
        });

    });

    describe('#_initialize', function(){

        var bindable;

        beforeEach(function () {

        });

        it('should initialize bindings in defaults', function(){
            var ExtendedBindable = C.Bindable.inherit({
                defaults: {
                    firstName: "Peter",
                    lastName: "Mustermann"
                },
                fullName: function(){
                    return this.$.firstName + " " + this.$.lastName;
                }.onChange('firstName','lastName')
            });

            bindable = new C.Bindable({
                name: 'Street 1',
                city: '{address.city}',
                fullName: '{address.subEntity.fullName()}',
                address: new C.Bindable({
                    city: "Leipzig",
                    subEntity: new ExtendedBindable()
                })
            });

            expect(bindable.$.city).to.be.equal(bindable.$.address.$.city);
            expect(bindable.$.fullName).to.be.equal(bindable.get('address.subEntity').fullName());
        });

    });

    describe('#_commit methods', function () {

        it('_commit method should be invoked once', function () {

            var MyBindable = C.Bindable.inherit({

                defaults: {
                    foo: "bar"
                },

                ctor: function() {
                    this.data = [];
                    this.callBase();
                },

                _commitFoo: function(value, oldValue) {
                    this.data.push({
                        value: value,
                        oldValue: oldValue
                    });
                }

            });

            var b = new MyBindable();

            expect(b.data).to.have.length(1);
            expect(b.data[0]).to.eql({
                value: "bar",
                oldValue: null
            });

        });

    })

});<commit_msg>fixed bindable tests<commit_after>var chai = require('chai'),
    should = chai.should(),
    expect = chai.expect,
    _ = require('underscore'),
    testRunner = require('..').TestRunner.setup();

var C = {};

describe('js.core.Bindable', function () {

    before(function (done) {
        testRunner.requireClasses({
            Bindable: 'js/core/Bindable',
            List: 'js/core/List'
        }, C, done);

    });

    describe('#defaults', function () {

        var BindableWithDefaults;

        before(function () {
            BindableWithDefaults = C.Bindable.inherit({
                defaults: {
                    foo: "bar",
                    x: 1,
                    list: C.List
                }
            });
        });

        it('new instance should have default attributes without modification of defaults', function () {
            var b = new BindableWithDefaults();
            expect(b).to.exist;
            expect(b.$.foo).to.exist.and.to.be.equal('bar');
            expect(b.$.x).to.exist.and.to.be.equal(1);
            expect(b.$.list).to.an.instanceof(C.List);

        });

        it('attributes set, should not be overwritten by defaults', function () {
            var b = new BindableWithDefaults({
                foo: 'foo'
            });

            expect(b).to.exist;
            expect(b.$.foo).to.exist.and.to.be.equal('foo');
            expect(b.$.x).to.exist.and.to.be.equal(1);
            expect(b.$.list).to.an.instanceof(C.List);

        });

        it('defaults should not be modified', function () {

            expect(BindableWithDefaults.prototype.defaults.foo).to.exist.and.to.be.equal('bar');
            expect(BindableWithDefaults.prototype.defaults.x).to.exist.and.to.be.equal(1);
            expect(BindableWithDefaults.prototype.defaults.list).to.exist.and.to.be.equal(C.List);
        })

    });

    describe('get', function () {

        var b,
            undefined;

        beforeEach(function () {
            b = new C.Bindable();
        });

        it('#empty get', function () {
            should.not.exist(b.get());
        });

        it('#one path $', function () {
            b.$.foo = "bar";
            var val = b.get('foo');
            should.exist(val);
            val.should.eql('bar');
        });

        it('#one path json', function () {
            var input = {
                foo: "bar"
            };

            var val = b.get(input, 'foo');
            should.exist(val);
            val.should.eql('bar');
        });

        it('#long path exists', function () {
            var input = {
                foo: {
                    bar: "zoo"
                }
            };

            var val = b.get(input, 'foo.bar');
            should.exist(val);
            val.should.eql('zoo');
        });

        it('#long path not exists', function () {
            var input = {
                foo: {
                    bar: "zoo"
                }
            };

            var val = b.get(input, 'foo.zoo');
            should.not.exist(val);
        });

        it('#fnc value foo.bar()', function () {
            var input = {
                foo: {
                    bar: function () {
                        return "hello"
                    }
                }
            };
            var val = b.get(input, 'foo.bar()');
            val.should.eql('hello');
        });

        it('#array ', function () {
            b.$.foo = [1, 2, 3];

            var val = b.get('foo[1]');
            should.exist(val);
            val.should.eql(2);
        });

        it('#array to self', function () {

            var val = b.get([0, 1, 2], '[1]');
            should.exist(val);
            val.should.eql(1);
        });

        it('#return array', function () {

            var a = b.$.foo = [1, 2, 3];

            var val = b.get('foo');
            should.exist(val);
            a.should.eql(val);
        });

        it('#return List', function () {

            var a = b.$.foo = new C.List([1, 2, 3]);

            var val = b.get('foo');
            should.exist(val);
            a.should.eql(val);
        });

        it('#List ', function () {
            b.$.foo = new C.List([3, 4, 5, 6]);

            var val = b.get('foo[2]');
            should.exist(val);
            val.should.eql(5);
        });

        it('#nested array ', function () {
            b.$.foo = [
                [0, 1, 2],
                [3, 4, 5],
                [6, 7, 8]
            ];

            var val = b.get('foo[1].[2]');
            should.exist(val);
            val.should.eql(5);
        });

        it('#not existing array path', function () {
            b.$.foo = [];

            var val = b.get('foo[1].bar');
            should.not.exist(val);
        });

    });

    describe('bind', function(){

        var bindable;

        it('#should bind to event and trigger callback on scope', function(done){

            bindable = new C.Bindable({
                foo: "bar"
            });

            var scope = {};

            bindable.bind('change', function(){

                expect(scope).to.equal(this);

                done();

            }, scope);

            bindable.set('foo',"newValue");
        });

        it('#should bind to event behind a path', function(done){

            bindable = new C.Bindable({
                nested: new C.Bindable({
                    foo: "bar"
                })
            });

            var scope = {};

            bindable.bind('nested','change', function(){

                expect(scope).to.equal(this);
                done();

            },scope);

            bindable.$.nested.set('foo', "newValue");
        });

        it('#should bind to an event with a function name', function(done){

            var bindable = new C.Bindable({
                foo: "bar"
            });

            var scope = {
                callback: function(){
                    expect(this).to.equal(scope);
                    done();
                }
            };

            bindable.bind('change',"callback()", scope);

            bindable.set('foo', "newValue");

        });

        it('#should bind to an event with a function name and parameters', function(done){
            var bindable = new C.Bindable({
                foo: "bar",
                foo2: "bar2"
            });

            var scope = {
                callback: function (event, foo, foo2, n, b, s) {
                    expect(this).to.equal(scope);
                    expect(event).to.exist;
                    expect(foo).to.equal(bindable.$.foo);
                    expect(foo2).to.equal(bindable.$.foo2);
                    expect(n).to.equal(5);
                    expect(b).to.equal(true);
                    expect(s).to.equal('test');
                    done();
                }
            };

            bindable.bind('change', "callback(event,foo,foo2,5,true,'test')", scope);
            bindable.set('foo', "newValue");
        });

    });

    describe('clone', function () {
        var original, nestedBindable, copy;
        beforeEach(function () {
            nestedBindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
            original = new C.Bindable({
                str: 'String',
                number: 123,
                boolean: true,
                person: {
                    firstName: 'Peter',
                    lastName: 'Pan'
                },
                nested: nestedBindable,
                arr: [
                    nestedBindable, "ab", 2, "waht", ["a", "b"]
                ]
            });

            copy = original.clone();
        });

        it('#should clone all attributes', function () {
            for (var key in original.$) {
                if (original.$.hasOwnProperty(key)) {
                    expect(copy.$[key]).to.exist;
                }
            }
        });

        it('#should create a deep copy of each nested Object', function () {
            expect(copy.$.person).to.exist.and.not.to.be.equal(original.$.person);
            expect(copy.$.person.firstName).to.exist.and.to.be.equal(original.$.person.firstName);
            expect(copy.$.person.lastName).to.exist.and.to.be.equal(original.$.person.lastName);

            original.$.person.firstName = "Hans";
            expect(copy.$.person.firstName).not.be.equal(original.$.person.firstName);
        });

        it('#should create a deep copy of each nested Array', function () {
            var val, copyVal;
            expect(copy.$.arr).to.exist;
            expect(copy.$.arr.length).to.be.equal(original.$.arr.length);

            for (var i = 0; i < original.$.arr.length; i++) {
                val = original.$.arr[i];
                copyVal = copy.$.arr[i];
                expect(copyVal).to.exist;
                if (val instanceof C.Bindable) {
                    expect(val).not.to.be.equal(copyVal);
                } else if (_.isArray(val)) {
                    expect(val.length).to.be.equal(copyVal.length);
                    expect(val).not.to.be.equal(copyVal);
                } else if (_.isObject(val)) {
                    expect(val).not.to.be.equal(copyVal);
                } else {
                    // numbers, strings, boolean and stuff
                    expect(val).to.be.equal(copyVal);
                }
            }
        });

        it('#should clone all nested Bindables', function () {
            expect(copy.$.nested).to.exist.and.not.to.be.equal(original.$.nested);
            expect(copy.$.nested.$.street).to.exist.and.to.be.equal(original.$.nested.$.street);
            expect(copy.$.nested.$.city).to.exist.and.to.be.equal(original.$.nested.$.city);
        });
    });

    describe('sync', function () {
        var original, nestedBindable, copy;
        beforeEach(function () {
            nestedBindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
            original = new C.Bindable({
                str: 'String',
                number: 123,
                boolean: true,
                person: {
                    firstName: 'Peter',
                    lastName: 'Pan'
                },
                nested: nestedBindable,
                arr: [
                    nestedBindable, "ab", 2, "waht", ["a", "b"]
                ]
            });

            copy = original.clone();
        });

        it('#should remove unset attributes from source', function () {
            copy.set('person',null,{unset: true});
            copy.set('nested', null, {unset: true});

            copy.sync();

            expect(original.$.person).not.to.exist;
            expect(original.$.nested).not.to.exist;
        });

        it('#should replace nested Bindable if they are new', function(){
            // create new Bindable
            copy.set('nested',new C.Bindable({
                street : 'A',
                city: 'B'
            }));

            copy.sync();

            expect(original.$.nested).not.to.be.equal(nestedBindable);
            expect(original.$.nested.$.street).to.be.equal(copy.$.nested.$.street);
            expect(original.$.nested.$.city).to.be.equal(copy.$.nested.$.city);

        });

        it('#should not replace nested Bindable if they were modified', function () {
            // modify nested Bindable
            copy.$.nested.set({
                street: 'A',
                city: 'B'
            });

            copy.sync();

            expect(original.$.nested).to.be.equal(nestedBindable);
            expect(original.$.nested.$.street).to.be.equal(copy.$.nested.$.street);
            expect(original.$.nested.$.city).to.be.equal(copy.$.nested.$.city);
        });
    });

    describe('#isDeepEqual', function(){
        var original, nestedBindable, copy;
        beforeEach(function () {
            nestedBindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
            original = new C.Bindable({
                str: 'String',
                number: 123,
                boolean: true,
                person: {
                    firstName: 'Peter',
                    lastName: 'Pan'
                },
                nested: nestedBindable,
                arr: [
                    nestedBindable, "ab", 2, "what", ["a", "b"]
                ]
            });

            copy = original.clone();
        });

        it('#exact clone should be equal', function () {
            expect(original.isDeepEqual(copy)).to.equal(true);
        });

        it('#modified clone with same values should be equal', function(){
            copy.set("arr", [
                nestedBindable.clone(),
                "ab",
                2,
                "what",
                ["a", "b"]
            ]);
            expect(original.isDeepEqual(copy)).to.equal(true);
        });

        it('#modified clone should not equal', function(){
            copy.set('number',312);
            expect(original.isDeepEqual(copy)).to.equal(false);
        });
    });

    describe('#destroy', function(){

        var bindable;

        beforeEach(function () {
            bindable = new C.Bindable({
                street: 'Street 1',
                city: 'City 1'
            });
        });

        it('should trigger destroy event', function(done){
            var destroyed = false;
            bindable.bind('destroy', function(){
                destroyed = true;
                done();
            });

            bindable.destroy();
            expect(destroyed).to.equal(true);
        });

        it('should remove all event listeners', function(){

            var triggered = false;

            bindable.bind('change', function(){
                triggered = true;
            });

            bindable.destroy();

            bindable.set('street','New Street');

            expect(triggered).to.equal(false);
        });

        it('should destroy all event bindables', function(){

            bindable.set('person', new C.Bindable({
                name: "Max"
            }));

            var triggered = 0;

            bindable.bind('person','change', function(){
                triggered++;
            });

            bindable.$.person.set('name','Peter');

            expect(triggered).to.be.equal(1);

            bindable.destroy();

            bindable.$.person.set('name','Max');

            expect(triggered).to.be.equal(1);
        });

    });

    describe('#_initialize', function(){

        var bindable;

        beforeEach(function () {

        });

        it('should initialize bindings in defaults when flag is set', function(){
            var ExtendedBindable = C.Bindable.inherit({
                defaults: {
                    firstName: "Peter",
                    lastName: "Mustermann"
                },
                fullName: function(){
                    return this.$.firstName + " " + this.$.lastName;
                }.onChange('firstName','lastName')
            });

            bindable = new C.Bindable({
                name: 'Street 1',
                city: '{address.city}',
                fullName: '{address.subEntity.fullName()}',
                address: new C.Bindable({
                    city: "Leipzig",
                    subEntity: new ExtendedBindable()
                })
            }, true);

            expect(bindable.$.city).to.be.equal(bindable.$.address.$.city);
            expect(bindable.$.fullName).to.be.equal(bindable.get('address.subEntity').fullName());
        });

    });

    describe('#_commit methods', function () {

        it('_commit method should be invoked once', function () {

            var MyBindable = C.Bindable.inherit({

                defaults: {
                    foo: "bar"
                },

                ctor: function() {
                    this.data = [];
                    this.callBase();
                },

                _commitFoo: function(value, oldValue) {
                    this.data.push({
                        value: value,
                        oldValue: oldValue
                    });
                }

            });

            var b = new MyBindable();

            expect(b.data).to.have.length(1);
            expect(b.data[0]).to.eql({
                value: "bar",
                oldValue: null
            });

        });

    })

});<|endoftext|>


<commit_before>var assert = require('assert');
var Context = require('../lib/Context');

describe('Context', function() {

    it("should merge objects", function() {
        assert_merge_context({a: 1}, undefined, {a: 1});
        assert_merge_context({a: 1}, null, {a: 1});
        assert_merge_context({a: 1}, {}, {a: 1});
        assert_merge_context({a: 1}, {b: 2}, {a: 1, b:2});
        assert_merge_context({a: 1}, {a: 2}, {a: 2});
    });

    it("should merge contexts", function() {
        assert.deepEqual(
            new Context({a: 1}).merge(new Context({b: 2})).properties,
            new Context({a:1, b: 2}).properties
        );
    });

    var assert_merge_context = function(thing1, thing2, expected) {
        assert.deepEqual(new Context(thing1).merge(thing2).properties, expected);
    }
});<commit_msg>Beefed up Context tests<commit_after>var assert = require('assert');
var Context = require('../lib/Context');

describe('Context', function() {

    it("should merge objects", function() {
        assert_merge_context({a: 1}, undefined, {a: 1});
        assert_merge_context({a: 1}, null, {a: 1});
        assert_merge_context({a: 1}, {}, {a: 1});
        assert_merge_context({a: 1}, {b: 2}, {a: 1, b:2});
        assert_merge_context({a: 1}, {a: 2}, {a: 2});

        assert_merge_context(undefined, {a: 1}, {a: 1});
        assert_merge_context(null, {a: 1}, {a: 1});
        assert_merge_context({}, {a: 1}, {a: 1});
        assert_merge_context({b: 2}, {a: 1}, {a: 1, b:2});
        assert_merge_context({a: 2}, {a: 1}, {a: 1});
    });

    it("should merge contexts", function() {
        assert.deepEqual(
            new Context({a: 1}).merge(new Context({b: 2})).properties,
            new Context({a:1, b: 2}).properties
        );
    });

    var assert_merge_context = function(thing1, thing2, expected) {
        assert.deepEqual(new Context(thing1).merge(thing2).properties, expected);
    }
});<|endoftext|>


<commit_before>var assert = require("assert")
  , config = require("./../config")
  , Sequelize = require("./../../index")
  , sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})

module.exports = {
  'it should correctly determine equal objects': function(exit) {
    var User = sequelize.define('User' + config.rand(), { name: Sequelize.STRING, bio: Sequelize.TEXT })

    User.sync({force: true}).on('success', function() {
      User.create({name: 'hallo', bio: 'welt'}).on('success', function(u) {
        assert.eql(u.equals(u), true)
        exit(function(){})
      }).on('failure', function(err) { console.log(err) })
    }).on('failure', function(err) { console.log(err) })
  },
  'it should correctly work with different primary keys': function(exit) {
    var User = sequelize.define('User' + config.rand(), {
      foo: {type: Sequelize.STRING, primaryKey: true},
      bar: {type: Sequelize.STRING, primaryKey: true},
      name: Sequelize.STRING, bio: Sequelize.TEXT
    })

    User.sync({force: true}).on('success', function() {
      User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).on('success', function(u) {
        assert.eql(u.equals(u), true)
        exit(function(){})
      })
    })
  },
  'equalsOneOf should work': function(exit) {
    var User = sequelize.define('User' + config.rand(), {
      foo: {type: Sequelize.STRING, primaryKey: true},
      bar: {type: Sequelize.STRING, primaryKey: true},
      name: Sequelize.STRING, bio: Sequelize.TEXT
    })

    User.sync({force: true}).on('success', function() {
      User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).on('success', function(u) {
        assert.eql(u.equalsOneOf([u, {a:1}]), true)
        assert.eql(u.equalsOneOf([{b:2}, {a:1}]), false)
        exit(function(){})
      })
    })
  }
}
<commit_msg>debugging for travis<commit_after>var assert = require("assert")
  , config = require("./../config")
  , Sequelize = require("./../../index")
  , sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})

module.exports = {
  'it should correctly determine equal objects': function(exit) {
    var User = sequelize.define('User' + config.rand(), { name: Sequelize.STRING, bio: Sequelize.TEXT })

    User.sync({force: true}).on('success', function() {
      User.create({name: 'hallo', bio: 'welt'}).on('success', function(u) {
        assert.eql(u.equals(u), true)
        exit(function(){})
      })
    })
  },
  'it should correctly work with different primary keys': function(exit) {
    var User = sequelize.define('User' + config.rand(), {
      foo: {type: Sequelize.STRING, primaryKey: true},
      bar: {type: Sequelize.STRING, primaryKey: true},
      name: Sequelize.STRING, bio: Sequelize.TEXT
    })

    User.sync({force: true}).on('success', function() {
      User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).on('success', function(u) {
        assert.eql(u.equals(u), true)
        exit(function(){})
      }).on('failure', function(err) { console.log(err) })
    }).on('failure', function(err) { console.log(err) })
  },
  'equalsOneOf should work': function(exit) {
    var User = sequelize.define('User' + config.rand(), {
      foo: {type: Sequelize.STRING, primaryKey: true},
      bar: {type: Sequelize.STRING, primaryKey: true},
      name: Sequelize.STRING, bio: Sequelize.TEXT
    })

    User.sync({force: true}).on('success', function() {
      User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).on('success', function(u) {
        assert.eql(u.equalsOneOf([u, {a:1}]), true)
        assert.eql(u.equalsOneOf([{b:2}, {a:1}]), false)
        exit(function(){})
      })
    })
  }
}
<|endoftext|>


<commit_before>if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
    var WebSocket = require('ws');

ParaComputeD = function(host) {

    this.ws = new WebSocket(host);
    this.opened = false;
    this.queue = [];
    this.qbacks = {};
    this.ws.paraParent = this;
    this.ws.onmessage = this.onmessage;
    this.ws.onopen = this.onopen;
    this.onopen=null;

};

ParaComputeD.prototype.isleep = function(qname) {

    var Obj = new Object();
    Obj.qname = qname;
    Obj.cmd = "ISLP";
    var data = JSON.stringify(Obj);
    console.log(Obj.qname + Obj.cmd);
    this.ws.send(data);

};
//var sendserver=func

ParaComputeD.prototype.onopen = function(evt) {

    //console.log(evt);
    this.opened = true;
    this.paraParent.onopen();

};

j = 1;
ParaComputeD.prototype.onmessage = function(evt) {

    console.log(evt.data);
    console.log(j++);
    d = JSON.parse(evt.data);

    if (typeof d.msg !== 'undefined') {
        // the variable is defined

        console.log("================Done==========");
    }
    if (d.cmd == "awk")
        this.paraParent.fetch(d.qname); //"this" is inside ws.onmessage scope. we need parent scope which is in the constructor :)
    if (d.cmd == 'nomsg') {
        this.paraParent.isleep(d.qname);

    }
    if(d.cmd=="msg"){
        this.paraParent.qbacks[d.qname](d.msg);
         this.paraParent.fetch(d.qname);
    }

};

ParaComputeD.prototype.publish = function(queue, msg, options) {

    var Obj = new Object();
    Obj.qname = queue;
    Obj.msg = String(msg);
    Obj.cmd = "PUB";
    Obj.delay= 0;
    Obj.expires= 0;

    if((typeof options !== 'undefined') && (typeof options.delay !== 'undefined')){

        Obj.delay = options.delay;
    }

    var data = JSON.stringify(Obj);
    this.ws.send(data);

};

ParaComputeD.prototype.sendSubCmd = function(queue) {

    var Obj = new Object();
    Obj.qname = queue;
    Obj.cmd = "SUB";
    var data = JSON.stringify(Obj);
    this.ws.send(data);

};

ParaComputeD.prototype.fetch = function(queue) {

    var Obj = new Object();
    Obj.qname = queue;
    Obj.cmd = "FET";
    var data = JSON.stringify(Obj);
    this.ws.send(data);

};

ParaComputeD.prototype.subscribe = function(queue, callback) {

    this.sendSubCmd(queue); //we have to notify the server that we are subscribing
    this.qbacks[queue] = callback;
    this.fetch(queue);

};

if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
    module.exports = ParaComputeD;
else
    window.ParaComputeD = ParaComputeD;
<commit_msg>removed para files<commit_after><|endoftext|>