<commit_before>"use strict";

////////////////////////////////////
//
//   Emulator Execution
//
////////////////////////////////////

var scaleFactor = 5;
var renderTarget = "target";

function unpackOptions(emulator, options) {
	var flags = [
		"tickrate",
		"fillColor",
		"fillColor2",
		"blendColor",
		"backgroundColor",
		"buzzColor",
		"quietColor",
		"shiftQuirks",
		"loadStoreQuirks",
		"vfOrderQuirks",
		"clipQuirks",
		"jumpQuirks",
		"enableXO",
		"screenRotation",
	]
	for (var x = 0; x < flags.length; x++) {
		var flag = flags[x];
		if (flag in options) { emulator[flag] = options[flag]; }
	}
}

function setRenderTarget(scale, canvas) {
	scaleFactor = scale;
	renderTarget = canvas;
	var c = document.getElementById(canvas);

	// Remove any existing previous delta frame so first frame is always drawn:
	c.last = undefined;

	var w  = scaleFactor * 128;
	var h  = scaleFactor *  64;
	var wm = (scaleFactor * -64) + "px";
	var hm = (scaleFactor * -32) + "px";

	if (emulator.screenRotation == 90 || emulator.screenRotation == 270) {
		c.width  = h;
		c.height = w;
		c.style.marginLeft = hm;
		c.style.marginTop  = wm;
	}
	else {
		c.width  = w;
		c.height = h;
		c.style.marginLeft = wm;
		c.style.marginTop  = hm;
	}
}

function getTransform(emulator, g) {
	g.setTransform(1, 0, 0, 1, 0, 0);
	var x = scaleFactor * 128;
	var y = scaleFactor *  64;
	switch(emulator.screenRotation) {
		case 90:
			g.rotate(0.5 * Math.PI);
			g.translate(0, -y);
			break;
		case 180:
			g.rotate(1.0 * Math.PI);
			g.translate(-x, -y);
			break;
		case 270:
			g.rotate(1.5 * Math.PI);
			g.translate(-x, 0);
			break;
		default:
			/* nothing to do */
	}
}

function arrayEqual(a, b) {
	var length = a.length;
	if (length !== b.length) { return false; }
	for (var i = 0; i < length; i++) {
		if (a[i] !== b[i]) { return false; }
	}
	return true;
}

function getColor(id) {
	switch(id) {
		case 0: return emulator.backgroundColor;
		case 1: return emulator.fillColor;
		case 2: return emulator.fillColor2;
		case 3: return emulator.blendColor;
	}
	throw "invalid color: " + id;
}

function renderDisplay(emulator) {
	var c = document.getElementById(renderTarget);

	// Canvas rendering can be expensive. Exit out early if nothing has changed.
	// NOTE: toggling emulator.hires changes emulator.p dimensions.
	var colors = [emulator.backColor, emulator.fillColor, emulator.fillColor2, emulator.blendColor];
	if (c.last !== undefined
			&& arrayEqual(c.last.p[0], emulator.p[0]) && arrayEqual(c.last.p[1], emulator.p[1])
			&& arrayEqual(c.last.colors, colors)) {
		return;
	}
	var g = c.getContext("2d");
	getTransform(emulator, g);
	var w      = emulator.hires ? 128         : 64;
	var h      = emulator.hires ? 64          : 32;
	var size   = emulator.hires ? scaleFactor : scaleFactor*2;
	var lastPixels = c.last !== undefined? c.last.p: [[], []]

	g.scale(size, size)
	var z = 0;
	for(var y = 0; y < h; ++y) {
		for(var x = 0; x < w; ++x, ++z) {
			var oldColorIdx = lastPixels[0][z] + (lastPixels[1][z] << 1);
			var colorIdx = emulator.p[0][z] + (emulator.p[1][z] << 1);
			if (oldColorIdx !== colorIdx) {
				g.fillStyle = getColor(colorIdx);
				g.fillRect(x, y, 1, 1);
			}
		}
	}
	g.scale(1, 1) //restore scale to 1,1 just in case

	c.last = {
		colors: colors,
		p: [emulator.p[0].slice(), emulator.p[1].slice()]
	};
}

////////////////////////////////////
//
//   Audio Playback
//
////////////////////////////////////

var audio;
var audioNode;
var audioSource;
var audioData;

var AudioBuffer = function(buffer, duration) {
	if (!(this instanceof AudioBuffer)) {
		return new AudioBuffer(buffer, duration);
	}

	this.pointer = 0;
	this.buffer = buffer;
	this.duration = duration;
}

AudioBuffer.prototype.write = function(buffer, index, size) {
	size = Math.max(0, Math.min(size, this.duration))
	if (!size) { return size; }

	this.duration -= size;
	var bufferSize = this.buffer.length;
	var end = index + size;

	for(var i = index; i < end; ++i) {
		buffer[i] = this.buffer[this.pointer++];
		this.pointer %= bufferSize;
	}

	return size;
}

AudioBuffer.prototype.dequeue = function(duration) {
	this.duration -= duration;
}

var FREQ = 4000;
var TIMER_FREQ = 60;
var SAMPLES = 16;
var BUFFER_SIZE = SAMPLES * 8

function audioSetup() {
	if (!audio) {
		if (typeof AudioContext !== 'undefined') {
			audio = new AudioContext();
		}
		else if (typeof webkitAudioContext !== 'undefined') {
			audio = new webkitAudioContext();
		}
	}
	if (audio && !audioNode) {
		audioNode = audio.createScriptProcessor(4096, 1, 1);
		audioNode.onaudioprocess = function(audioProcessingEvent) {
			var outputBuffer = audioProcessingEvent.outputBuffer;
			var outputData = outputBuffer.getChannelData(0);
			var samples_n = outputBuffer.length;

			var index = 0;
			while(audioData.length && index < samples_n) {
				var size = samples_n - index;
				var written = audioData[0].write(outputData, index, size);
				index += written;
				if (written < size) {
					audioData.shift();
				}
			}

			while(index < samples_n) {
				outputData[index++] = 0;
			}
			//the last one can be long sound with high value of buzzer, so always keep it
			if (audioData.length > 1) {
				var audioDataSize = 0;
				var audioBufferSize = audioNode.bufferSize;
				audioData.forEach(function(buffer) { audioDataSize += buffer.duration; })
				while(audioDataSize > audioBufferSize && audioData.length > 1) {
					audioDataSize -= audioData.shift().duration;
				}
			}
		}
		audioData = [];
		audioNode.connect(audio.destination);
		return true;
	}
	if (audio && audioNode) { return true; }
	return false;
}

function stopAudio() {
	if (!audio) { return; }
	if (audioNode) {
		audioNode.disconnect();
		audioNode = null;
	}
	audioData = [];
}

var VOLUME = 0.25;

function playPattern(soundLength, buffer, remainingTicks) {
	if (!audio) { return; }

	var samples = Math.floor(BUFFER_SIZE * audio.sampleRate / FREQ);
	var audioBuffer = new Array(samples);
	if (remainingTicks && audioData.length > 0) {
		audioData[audioData.length - 1].dequeue(Math.floor(remainingTicks * audio.sampleRate / TIMER_FREQ));
	}

	for(var i = 0; i < samples; ++i) {
		var srcIndex = Math.floor(i * FREQ / audio.sampleRate);
		var cell = srcIndex >> 3;
		var bit = srcIndex & 7;
		audioBuffer[i] = (buffer[srcIndex >> 3] & (0x80 >> bit)) ? VOLUME: 0;
	}
	audioData.push(new AudioBuffer(audioBuffer, Math.floor(soundLength * audio.sampleRate / TIMER_FREQ)));
}

function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
}
<commit_msg>reset c.last if resolution changed<commit_after>"use strict";

////////////////////////////////////
//
//   Emulator Execution
//
////////////////////////////////////

var scaleFactor = 5;
var renderTarget = "target";

function unpackOptions(emulator, options) {
	var flags = [
		"tickrate",
		"fillColor",
		"fillColor2",
		"blendColor",
		"backgroundColor",
		"buzzColor",
		"quietColor",
		"shiftQuirks",
		"loadStoreQuirks",
		"vfOrderQuirks",
		"clipQuirks",
		"jumpQuirks",
		"enableXO",
		"screenRotation",
	]
	for (var x = 0; x < flags.length; x++) {
		var flag = flags[x];
		if (flag in options) { emulator[flag] = options[flag]; }
	}
}

function setRenderTarget(scale, canvas) {
	scaleFactor = scale;
	renderTarget = canvas;
	var c = document.getElementById(canvas);

	// Remove any existing previous delta frame so first frame is always drawn:
	c.last = undefined;

	var w  = scaleFactor * 128;
	var h  = scaleFactor *  64;
	var wm = (scaleFactor * -64) + "px";
	var hm = (scaleFactor * -32) + "px";

	if (emulator.screenRotation == 90 || emulator.screenRotation == 270) {
		c.width  = h;
		c.height = w;
		c.style.marginLeft = hm;
		c.style.marginTop  = wm;
	}
	else {
		c.width  = w;
		c.height = h;
		c.style.marginLeft = wm;
		c.style.marginTop  = hm;
	}
}

function getTransform(emulator, g) {
	g.setTransform(1, 0, 0, 1, 0, 0);
	var x = scaleFactor * 128;
	var y = scaleFactor *  64;
	switch(emulator.screenRotation) {
		case 90:
			g.rotate(0.5 * Math.PI);
			g.translate(0, -y);
			break;
		case 180:
			g.rotate(1.0 * Math.PI);
			g.translate(-x, -y);
			break;
		case 270:
			g.rotate(1.5 * Math.PI);
			g.translate(-x, 0);
			break;
		default:
			/* nothing to do */
	}
}

function arrayEqual(a, b) {
	var length = a.length;
	if (length !== b.length) { return false; }
	for (var i = 0; i < length; i++) {
		if (a[i] !== b[i]) { return false; }
	}
	return true;
}

function getColor(id) {
	switch(id) {
		case 0: return emulator.backgroundColor;
		case 1: return emulator.fillColor;
		case 2: return emulator.fillColor2;
		case 3: return emulator.blendColor;
	}
	throw "invalid color: " + id;
}

function renderDisplay(emulator) {
	var c = document.getElementById(renderTarget);

	// Canvas rendering can be expensive. Exit out early if nothing has changed.
	// NOTE: toggling emulator.hires changes emulator.p dimensions.
	var colors = [emulator.backColor, emulator.fillColor, emulator.fillColor2, emulator.blendColor];
	if (c.last !== undefined) {
		if (arrayEqual(c.last.p[0], emulator.p[0]) && arrayEqual(c.last.p[1], emulator.p[1])
				&& arrayEqual(c.last.colors, colors)) {
			return;
		}
		if (c.last.p[0].length != emulator.p[0].length)
			c.last = undefined
	}
	var g = c.getContext("2d");
	getTransform(emulator, g);
	var w      = emulator.hires ? 128         : 64;
	var h      = emulator.hires ? 64          : 32;
	var size   = emulator.hires ? scaleFactor : scaleFactor*2;
	var lastPixels = c.last !== undefined? c.last.p: [[], []]

	g.scale(size, size)
	var z = 0;
	for(var y = 0; y < h; ++y) {
		for(var x = 0; x < w; ++x, ++z) {
			var oldColorIdx = lastPixels[0][z] + (lastPixels[1][z] << 1);
			var colorIdx = emulator.p[0][z] + (emulator.p[1][z] << 1);
			if (oldColorIdx !== colorIdx) {
				g.fillStyle = getColor(colorIdx);
				g.fillRect(x, y, 1, 1);
			}
		}
	}
	g.scale(1, 1) //restore scale to 1,1 just in case

	c.last = {
		colors: colors,
		p: [emulator.p[0].slice(), emulator.p[1].slice()]
	};
}

////////////////////////////////////
//
//   Audio Playback
//
////////////////////////////////////

var audio;
var audioNode;
var audioSource;
var audioData;

var AudioBuffer = function(buffer, duration) {
	if (!(this instanceof AudioBuffer)) {
		return new AudioBuffer(buffer, duration);
	}

	this.pointer = 0;
	this.buffer = buffer;
	this.duration = duration;
}

AudioBuffer.prototype.write = function(buffer, index, size) {
	size = Math.max(0, Math.min(size, this.duration))
	if (!size) { return size; }

	this.duration -= size;
	var bufferSize = this.buffer.length;
	var end = index + size;

	for(var i = index; i < end; ++i) {
		buffer[i] = this.buffer[this.pointer++];
		this.pointer %= bufferSize;
	}

	return size;
}

AudioBuffer.prototype.dequeue = function(duration) {
	this.duration -= duration;
}

var FREQ = 4000;
var TIMER_FREQ = 60;
var SAMPLES = 16;
var BUFFER_SIZE = SAMPLES * 8

function audioSetup() {
	if (!audio) {
		if (typeof AudioContext !== 'undefined') {
			audio = new AudioContext();
		}
		else if (typeof webkitAudioContext !== 'undefined') {
			audio = new webkitAudioContext();
		}
	}
	if (audio && !audioNode) {
		audioNode = audio.createScriptProcessor(4096, 1, 1);
		audioNode.onaudioprocess = function(audioProcessingEvent) {
			var outputBuffer = audioProcessingEvent.outputBuffer;
			var outputData = outputBuffer.getChannelData(0);
			var samples_n = outputBuffer.length;

			var index = 0;
			while(audioData.length && index < samples_n) {
				var size = samples_n - index;
				var written = audioData[0].write(outputData, index, size);
				index += written;
				if (written < size) {
					audioData.shift();
				}
			}

			while(index < samples_n) {
				outputData[index++] = 0;
			}
			//the last one can be long sound with high value of buzzer, so always keep it
			if (audioData.length > 1) {
				var audioDataSize = 0;
				var audioBufferSize = audioNode.bufferSize;
				audioData.forEach(function(buffer) { audioDataSize += buffer.duration; })
				while(audioDataSize > audioBufferSize && audioData.length > 1) {
					audioDataSize -= audioData.shift().duration;
				}
			}
		}
		audioData = [];
		audioNode.connect(audio.destination);
		return true;
	}
	if (audio && audioNode) { return true; }
	return false;
}

function stopAudio() {
	if (!audio) { return; }
	if (audioNode) {
		audioNode.disconnect();
		audioNode = null;
	}
	audioData = [];
}

var VOLUME = 0.25;

function playPattern(soundLength, buffer, remainingTicks) {
	if (!audio) { return; }

	var samples = Math.floor(BUFFER_SIZE * audio.sampleRate / FREQ);
	var audioBuffer = new Array(samples);
	if (remainingTicks && audioData.length > 0) {
		audioData[audioData.length - 1].dequeue(Math.floor(remainingTicks * audio.sampleRate / TIMER_FREQ));
	}

	for(var i = 0; i < samples; ++i) {
		var srcIndex = Math.floor(i * FREQ / audio.sampleRate);
		var cell = srcIndex >> 3;
		var bit = srcIndex & 7;
		audioBuffer[i] = (buffer[srcIndex >> 3] & (0x80 >> bit)) ? VOLUME: 0;
	}
	audioData.push(new AudioBuffer(audioBuffer, Math.floor(soundLength * audio.sampleRate / TIMER_FREQ)));
}

function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
}
<|endoftext|>


<commit_before>'use strict';

/* eslint no-console: 0, no-process-env: 0 */

var fs = require('fs');
var path = require('path');

var buildPath = path.join(process.env.ANTITAMPERING_TEST_DIR, 'platforms/ios');
try {
    buildPath = path.join(buildPath);
    fs.statSync(buildPath).isDirectory();
} catch (e) {
    buildPath = path.join(buildPath, 'build/emulator/HelloWorld.app');
}

var indexAsset = path.join(buildPath, 'www/index.html');
var content;

try {
    content = fs.readFileSync(indexAsset, 'utf-8');
} catch (e) {
    throw new Error('Unable to read original index.html from ios app', e);
}

var tamperedContent = content.replace(/
(.+)<\/h1>/, function (match, group) {
    var result = match.replace(group, '' + group + '');
    return result + '
I\'ve been tampered with!
';
});

try {
    fs.writeFileSync(indexAsset, tamperedContent, 'utf-8');
    console.log('The original ios build was successfully tampered with.');
} catch (e) {
    throw new Error('Unable to write over index.html in ios app', e);
}
<commit_msg>Fix tamper test ios<commit_after>'use strict';

/* eslint no-console: 0, no-process-env: 0 */

var fs = require('fs');
var path = require('path');

var buildPath = path.join(process.env.ANTITAMPERING_TEST_DIR, 'platforms/ios');
try {
    buildPath = path.join(buildPath);
    fs.statSync(path.join(buildPath, 'www')).isDirectory();
} catch (e) {
    buildPath = path.join(buildPath, 'build/emulator/HelloWorld.app');
}

var indexAsset = path.join(buildPath, 'www/index.html');
var content;

try {
    content = fs.readFileSync(indexAsset, 'utf-8');
} catch (e) {
    throw new Error('Unable to read original index.html from ios app', e);
}

var tamperedContent = content.replace(/
(.+)<\/h1>/, function (match, group) {
    var result = match.replace(group, '' + group + '');
    return result + '
I\'ve been tampered with!
';
});

try {
    fs.writeFileSync(indexAsset, tamperedContent, 'utf-8');
    console.log('The original ios build was successfully tampered with.');
} catch (e) {
    throw new Error('Unable to write over index.html in ios app', e);
}
<|endoftext|>


<commit_before>var socket = io();

$(document).ready(function() {

  $(document).on('click', '.room', function(event) {
    $('#room_tabs>li').removeClass('focus');
    $('#' + event.target.id).addClass('focus');
    socket.emit('change room', event.target.id);
      });

  $('#message').keypress(function(e) {
    if (e.which === 13) {
      $('#send').click();
    }
  });
  $(document).on('click', '.exit', function(event) {
    $("#" + event.target.id).remove();
    socket.emit('unsubscribe', event.target.id);
  });
  $('#change_username').click(function() {
    var newname =  prompt("What is your new username?")
    socket.emit('change username', {nick: newname, room: $("#chat").data('room')})
  })

  $('#send').click(function() {
    var msg = $('#message').val();
    if (msg === '') return false;
    if ($("#chat").data('room') === '') {
      socket.emit('no rooms');
    }
    $('#message').val('');
    socket.emit('message', {
      msg: msg,
      room: $('#chat').data('room')
    });
});
});

/* When the user connects and disconnects.
 */
socket.on('connect', function() {
  socket.emit('pick username', prompt("What is your username?"));
});

/* When the user makes their name, check to see if
 * the name is taken or blank. If not, join lobby.
 */

socket.on('join successful', function(nick) {
  $("#username").html("
" +nick + "
");
})

socket.on('username error', function(err) {
  socket.emit('pick username', prompt(err));
});

/* When the user changes rooms.
 */

socket.on('change room', function(data) {
  $('#messages').html('');
  $("#userlist").html('');
  $('#chat').data('room', data.room)
  $('#messages').append(data.msgs);
  $('#userlist').append(data.users+"");
});

/* When the user either
 * joins a room or leaves a room
 */

socket.on('subscribe', function(room) {
  $('#chat').data('room', room);
  if (room === 'lobby') {
    $('#room_tabs').append('
lobby
');
  } else {
    $('#room_tabs').append('
' + room + '
');
  }

});

socket.on('unsubscribe', function(room) {
  if ($("#chat").data('room') === room) {
    $('#chat').data('room', null);
  };
  $("#" + room).remove();
})

socket.on('user joined room', function(data) {
  var msg = "
" + data.nick + " joined
";
  $('#messages').append(msg);
  $("#userlist").append("
"+data.users.join('
')+"
");

});

socket.on('user left room', function(data) {
  var msg = "
" + data.nick + " left
";
  $('#messages').append(msg);
  $("#userlist").append("
"+data.users.join('
')+"
");
});

socket.on('user changed name', function(data) {
  var msg = "
"+data.old+" is now "+data.current +"
";
  $('#messages').append(msg);
  $('#username').html('
'+data.current+'
');
  $('#userlist').html('');
  $("#userlist").append("
"+data.users.join("
")+"
");
})

/* When the user attempts to sends a message to a room.
 */

socket.on('message', function(msg) {
  $('#messages').append(msg);

});<commit_msg>Reset userlist on join.<commit_after>var socket = io();

$(document).ready(function() {

  $(document).on('click', '.room', function(event) {
    $('#room_tabs>li').removeClass('focus');
    $('#' + event.target.id).addClass('focus');
    socket.emit('change room', event.target.id);
      });

  $('#message').keypress(function(e) {
    if (e.which === 13) {
      $('#send').click();
    }
  });
  $(document).on('click', '.exit', function(event) {
    $("#" + event.target.id).remove();
    socket.emit('unsubscribe', event.target.id);
  });
  $('#change_username').click(function() {
    var newname =  prompt("What is your new username?")
    socket.emit('change username', {nick: newname, room: $("#chat").data('room')})
  })

  $('#send').click(function() {
    var msg = $('#message').val();
    if (msg === '') return false;
    if ($("#chat").data('room') === '') {
      socket.emit('no rooms');
    }
    $('#message').val('');
    socket.emit('message', {
      msg: msg,
      room: $('#chat').data('room')
    });
});
});

/* When the user connects and disconnects.
 */
socket.on('connect', function() {
  socket.emit('pick username', prompt("What is your username?"));
});

/* When the user makes their name, check to see if
 * the name is taken or blank. If not, join lobby.
 */

socket.on('join successful', function(nick) {
  $("#username").html("
" +nick + "
");
})

socket.on('username error', function(err) {
  socket.emit('pick username', prompt(err));
});

/* When the user changes rooms.
 */

socket.on('change room', function(data) {
  $('#messages').html('');
  $("#userlist").html('');
  $('#chat').data('room', data.room)
  $('#messages').append(data.msgs);
  $('#userlist').append(data.users+"");
});

/* When the user either
 * joins a room or leaves a room
 */

socket.on('subscribe', function(room) {
  $('#chat').data('room', room);
  if (room === 'lobby') {
    $('#room_tabs').append('
lobby
');
  } else {
    $('#room_tabs').append('
' + room + '
');
  }

});

socket.on('unsubscribe', function(room) {
  if ($("#chat").data('room') === room) {
    $('#chat').data('room', null);
  };
  $("#" + room).remove();
})

socket.on('user joined room', function(data) {
  var msg = "
" + data.nick + " joined
";
  $('#messages').append(msg);
  $('#userlist').html('');
  $("#userlist").append("
"+data.users.join('
')+"
");

});

socket.on('user left room', function(data) {
  var msg = "
" + data.nick + " left
";
  $('#messages').append(msg);
  $("#userlist").append("
"+data.users.join('
')+"
");
});

socket.on('user changed name', function(data) {
  var msg = "
"+data.old+" is now "+data.current +"
";
  $('#messages').append(msg);
  $('#username').html('
'+data.current+'
');
  $('#userlist').html('');
  $("#userlist").append("
"+data.users.join("
")+"
");
})

/* When the user attempts to sends a message to a room.
 */

socket.on('message', function(msg) {
  $('#messages').append(msg);

});<|endoftext|>


<commit_before>
(function($) {

    $.fn.tagit = function(options) {

        var settings = {
            'itemName'          : 'item',
            'fieldName'         : 'tags',
            'availableTags'     : [],
            // callback: called when a tag is added
            'onTagAdded'        : null,
            // callback: called when a tag is removed
            'onTagRemoved'      : null,
            // callback: called when a tag is clicked
    	    'onTagClicked'      : null,
            'tagSource':
                function(search, show_choices) {
                    var filter = new RegExp(search.term, 'i');
                    var choices = settings.availableTags.filter(function(element) {
                        return (element.search(filter) != -1);
                    });
                    show_choices(subtract_array(choices, assigned_tags()));
                },
            'removeConfirmation': false,
            'caseSensitive': true,
            'allowSpaces': false, // when enabled, quotes are not neccesary for inputting multi-word tags

            'singleField': false, // When enabled, will use a single hidden field for the form, rather than one per tag.
                                  // It will delimit tags in the field with singleFieldDelimiter.
            'singleFieldDelimiter': ','
        };

        if (options) {
            $.extend(settings, options);
        }

        var tagList = $(this),
            tagInput  = $('<input class="tagit-input" type="text" />');
            BACKSPACE = 8,
            ENTER     = 13,
            SPACE     = 32,
            COMMA     = 44,
            TAB       = 9;

        tagList
            // add the tagit CSS class.
            .addClass('tagit')
            // create the input field.
            .append($('

\n').append(tagInput))
            .click(function(e) {
                if (e.target.className == 'close') {
                    // Removes a tag when the little 'x' is clicked.
                    // Event is binded to the UL, otherwise a new tag (LI > A) wouldn't have this event attached to it.
                    remove_tag($(e.target).parent());
                } else if (e.target.className == 'tagLabel' && settings.onTagClicked) {
                    settings.onTagClicked($(e.target).parent());
                } else {
                    // Sets the focus() to the input field, if the user clicks anywhere inside the UL.
                    // This is needed because the input field needs to be of a small size.
                    tagInput.focus();
                }
            })
            // add existing tags
            .children('li')
                .each(function() {
                    if (!$(this).hasClass('tagit-new')) {
                        create_tag($(this).html(), $(this).attr('class'));
                        $(this).remove();
                    }
                });

        tagInput
            .keydown(function(event) {
                var keyCode = event.keyCode || event.which;
                // Backspace is not detected within a keypress, so using a keydown
                if (keyCode == BACKSPACE && tagInput.val() == '') {
                    var tag = tagList.children('.tagit-choice:last');
                    if (!settings.removeConfirmation || tag.hasClass('remove')) {
                        // When backspace is pressed, the last tag is deleted.
                        remove_tag(tag);
                    } else if (settings.removeConfirmation) {
                        tag.addClass('remove');
                    }
                }
            })
            .keypress(function(event) {
                var keyCode = event.keyCode || event.which;
                // Comma/Space/Enter are all valid delimiters for new tags. except when there is an open quote or if setting allowSpaces = true
                if (
                    keyCode == COMMA ||
                    keyCode == ENTER ||
                    keyCode == TAB ||
                    (
             	        keyCode == SPACE &&
             	        settings.allowSpaces != true &&
				        (
					        ($.trim(tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"') ||
					        (
					            $.trim(tagInput.val()).charAt(0) == '"' &&
					            $.trim(tagInput.val()).charAt(tagInput.val().trim().length - 1) == '"' &&
					            $.trim(tagInput.val()).length - 1 != 0
					        )
				        )
                    )
                ) {

                    event.preventDefault();
                    $.trim(create_tag(tagInput.val().replace(/^'|"$|,+$/g, '')));
                }
                if (settings.removeConfirmation) {
                    tagList.children('.tagit-choice:last').removeClass('remove');
                }
            });

        if (options.availableTags || options.tagSource) {
            tagInput.autocomplete({
                source: settings.tagSource,
                select: function(event, ui) {
                    create_tag(ui.item.value);
                    // Preventing the tag input to be updated with the chosen value.
                    return false;
                }
            });
        }

        function assigned_tags() {
            var tags = [];
            tagList.children('.tagit-choice').each(function() {
                tags.push($(this).children('input').val());
            });
            return tags;
        }

        function subtract_array(a1, a2) {
            var result = new Array();
            for (var i = 0; i < a1.length; i++) {
                if (a2.indexOf(a1[i]) == -1) {
                    result.push(a1[i]);
                }
            }
            return result;
        }

        function is_new(value) {
            var isNew = true;
            tagList.children('.tagit-choice').each(function(i) {
                if (format_str(value) == format_str($(this).children('input').val())) {
                    isNew = false;
                    return;
                }
            });
            return isNew;
        }
	    function format_str(str) {
		    if(settings.caseSensitive)
			    return str;
		    return str.toLowerCase();
	    }
        function create_tag(value, additionalClass) {
            // Cleaning the input.
            tagInput.val('');

            if (!is_new(value) || value == '') {
                return false;
            }
            var linkValue = value;
            if (settings.onTagClicked) {
      	        linkValue = '' + value + '';
            }
            // create tag
            var tag = $('
')
                .addClass('tagit-choice')
                .addClass(additionalClass)
                .append(linkValue)
                .append('x');
            if (settings.singleField) {
                //TODO
            } else {
                tag.append('<input type="hidden" style="display:none;" value="' + value + '" name="' + settings.itemName + '[' + settings.fieldName + '][]">');
            }

            if (settings.onTagAdded) {
                settings.onTagAdded(tag);
            }

            // insert tag
            tagInput.parent().before(tag);
        }
        function remove_tag(tag) {
            if (settings.onTagRemoved) {
                settings.onTagRemoved(tag);
            }
            if (settings.singleField) {
                //TODO
            }
            tag.remove();
        }

        // maintaining chainability
        return this;
    };

})(jQuery);

<commit_msg>camel caseification<commit_after>
(function($) {

    $.fn.tagit = function(options) {

        var settings = {
            'itemName'          : 'item',
            'fieldName'         : 'tags',
            'availableTags'     : [],
            // callback: called when a tag is added
            'onTagAdded'        : null,
            // callback: called when a tag is removed
            'onTagRemoved'      : null,
            // callback: called when a tag is clicked
    	    'onTagClicked'      : null,
            'tagSource':
                function(search, showChoices) {
                    var filter = new RegExp(search.term, 'i');
                    var choices = settings.availableTags.filter(function(element) {
                        return (element.search(filter) != -1);
                    });
                    showChoices(subtractArray(choices, assignedTags()));
                },
            'removeConfirmation': false,
            'caseSensitive': true,
            'allowSpaces': false, // when enabled, quotes are not neccesary for inputting multi-word tags

            'singleField': false, // When enabled, will use a single hidden field for the form, rather than one per tag.
                                  // It will delimit tags in the field with singleFieldDelimiter.
            'singleFieldDelimiter': ','
        };

        if (options) {
            $.extend(settings, options);
        }

        var tagList = $(this),
            tagInput  = $('<input class="tagit-input" type="text" />');
            BACKSPACE = 8,
            ENTER     = 13,
            SPACE     = 32,
            COMMA     = 44,
            TAB       = 9;

        tagList
            // add the tagit CSS class.
            .addClass('tagit')
            // create the input field.
            .append($('

\n').append(tagInput))
            .click(function(e) {
                if (e.target.className == 'close') {
                    // Removes a tag when the little 'x' is clicked.
                    // Event is binded to the UL, otherwise a new tag (LI > A) wouldn't have this event attached to it.
                    removeTag($(e.target).parent());
                } else if (e.target.className == 'tagLabel' && settings.onTagClicked) {
                    settings.onTagClicked($(e.target).parent());
                } else {
                    // Sets the focus() to the input field, if the user clicks anywhere inside the UL.
                    // This is needed because the input field needs to be of a small size.
                    tagInput.focus();
                }
            })
            // add existing tags
            .children('li')
                .each(function() {
                    if (!$(this).hasClass('tagit-new')) {
                        createTag($(this).html(), $(this).attr('class'));
                        $(this).remove();
                    }
                });

        tagInput
            .keydown(function(event) {
                var keyCode = event.keyCode || event.which;
                // Backspace is not detected within a keypress, so using a keydown
                if (keyCode == BACKSPACE && tagInput.val() == '') {
                    var tag = tagList.children('.tagit-choice:last');
                    if (!settings.removeConfirmation || tag.hasClass('remove')) {
                        // When backspace is pressed, the last tag is deleted.
                        removeTag(tag);
                    } else if (settings.removeConfirmation) {
                        tag.addClass('remove');
                    }
                }
            })
            .keypress(function(event) {
                var keyCode = event.keyCode || event.which;
                // Comma/Space/Enter are all valid delimiters for new tags. except when there is an open quote or if setting allowSpaces = true
                if (
                    keyCode == COMMA ||
                    keyCode == ENTER ||
                    keyCode == TAB ||
                    (
             	        keyCode == SPACE &&
             	        settings.allowSpaces != true &&
				        (
					        ($.trim(tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"') ||
					        (
					            $.trim(tagInput.val()).charAt(0) == '"' &&
					            $.trim(tagInput.val()).charAt(tagInput.val().trim().length - 1) == '"' &&
					            $.trim(tagInput.val()).length - 1 != 0
					        )
				        )
                    )
                ) {

                    event.preventDefault();
                    $.trim(createTag(tagInput.val().replace(/^'|"$|,+$/g, '')));
                }
                if (settings.removeConfirmation) {
                    tagList.children('.tagit-choice:last').removeClass('remove');
                }
            });

        if (options.availableTags || options.tagSource) {
            tagInput.autocomplete({
                source: settings.tagSource,
                select: function(event, ui) {
                    createTag(ui.item.value);
                    // Preventing the tag input to be updated with the chosen value.
                    return false;
                }
            });
        }

        function assignedTags() {
            // Returns an array of tag string values
            var tags = [];
            tagList.children('.tagit-choice').each(function() {
                tags.push($(this).children('input').val());
            });
            return tags;
        }

        function subtractArray(a1, a2) {
            var result = new Array();
            for (var i = 0; i < a1.length; i++) {
                if (a2.indexOf(a1[i]) == -1) {
                    result.push(a1[i]);
                }
            }
            return result;
        }

        function isNew(value) {
            var isNew = true;
            tagList.children('.tagit-choice').each(function(i) {
                if (formatStr(value) == formatStr($(this).children('input').val())) {
                    isNew = false;
                    return;
                }
            });
            return isNew;
        }
	    function formatStr(str) {
		    if(settings.caseSensitive)
			    return str;
		    return str.toLowerCase();
	    }
        function createTag(value, additionalClass) {
            // Cleaning the input.
            tagInput.val('');

            if (!isNew(value) || value == '') {
                return false;
            }
            var linkValue = value;
            if (settings.onTagClicked) {
      	        linkValue = '' + value + '';
            }
            // create tag
            var tag = $('
')
                .addClass('tagit-choice')
                .addClass(additionalClass)
                .append(linkValue)
                .append('x');
            if (settings.singleField) {
                //TODO
            } else {
                tag.append('<input type="hidden" style="display:none;" value="' + value + '" name="' + settings.itemName + '[' + settings.fieldName + '][]">');
            }

            if (settings.onTagAdded) {
                settings.onTagAdded(tag);
            }

            // insert tag
            tagInput.parent().before(tag);
        }
        function removeTag(tag) {
            if (settings.onTagRemoved) {
                settings.onTagRemoved(tag);
            }
            if (settings.singleField) {
                //TODO
            }
            tag.remove();
        }

        // maintaining chainability
        return this;
    };

})(jQuery);

<|endoftext|>


<commit_before>/*
* jQuery UI Tag-it!
*
* @version v2.0 (06/2011)
*
* Copyright 2011, Levy Carneiro Jr.
* Released under the MIT license.
* http://aehlke.github.com/tag-it/LICENSE
*
* Homepage:
*   http://aehlke.github.com/tag-it/
*
* Authors:
*   Levy Carneiro Jr.
*   Martin Rehfeld
*   Tobias Schmidt
*   Skylar Challand
*   Alex Ehlke
*
* Maintainer:
*   Alex Ehlke - Twitter: @aehlke
*
* Dependencies:
*   jQuery v1.4+
*   jQuery UI v1.8+
*/
(function($) {

    $.widget('ui.tagit', {
        options: {
            fieldName         : 'tags',
            availableTags     : [],
            tagSource         : null,
            removeConfirmation: false,
            caseSensitive     : true,
            placeholderText   : null,
            allowDuplicates   : false,

            // When enabled, quotes are not neccesary
            // for inputting multi-word tags.
            allowSpaces: false,

            // Whether to animate tag removals or not.
            animate: true,

            // The below options are for using a single field instead of several
            // for our form values.
            //
            // When enabled, will use a single hidden field for the form,
            // rather than one per tag. It will delimit tags in the field
            // with singleFieldDelimiter.
            //
            // The easiest way to use singleField is to just instantiate tag-it
            // on an INPUT element, in which case singleField is automatically
            // set to true, and singleFieldNode is set to that element. This
            // way, you don't need to fiddle with these options.
            singleField: false,

            singleFieldDelimiter: ',',

            // Set this to an input DOM node to use an existing form field.
            // Any text in it will be erased on init. But it will be
            // populated with the text of tags as they are created,
            // delimited by singleFieldDelimiter.
            //
            // If this is not set, we create an input node for it,
            // with the name given in settings.fieldName.
            singleFieldNode: null,

            // Optionally set a tabindex attribute on the input that gets
            // created for tag-it.
            tabIndex: null,

            // Event callbacks.
            onTagAdded  : null,
            onTagRemoved: null,
            onTagClicked: null
        },

        _create: function() {
            // for handling static scoping inside callbacks
            var that = this;

            // There are 2 kinds of DOM nodes this widget can be instantiated on:
            //     1. UL, OL, or some element containing either of these.
            //     2. INPUT, in which case 'singleField' is overridden to true,
            //        a UL is created and the INPUT is hidden.
            if (this.element.is('input')) {
                this.tagList = $('

').insertAfter(this.element);
                this.options.singleField = true;
                this.options.singleFieldNode = this.element;
                this.element.css('display', 'none');
            } else {
                this.tagList = this.element.find('ul, ol').andSelf().last();
            }

            this.tagInput = $('<input type="text" />').addClass('ui-widget-content');
            if (this.options.tabIndex) {
                this.tagInput.attr('tabindex', this.options.tabIndex);
            }
            if (this.options.placeholderText) {
                this.tagInput.attr('placeholder', this.options.placeholderText);
            }

            this.options.tagSource = this.options.tagSource || function(search, showChoices) {
                var filter = search.term.toLowerCase();
                var choices = $.grep(this.options.availableTags, function(element) {
                    // Only match autocomplete options that begin with the search term.
                    // (Case insensitive.)
                    return (element.toLowerCase().indexOf(filter) === 0);
                });
                showChoices(this._subtractArray(choices, this.assignedTags()));
            };

            // Bind tagSource callback functions to this context.
            if ($.isFunction(this.options.tagSource)) {
                this.options.tagSource = $.proxy(this.options.tagSource, this);
            }

            this.tagList
                .addClass('tagit')
                .addClass('ui-widget ui-widget-content ui-corner-all')
                // Create the input field.
                .append($('

').append(this.tagInput))
                .click(function(e) {
                    var target = $(e.target);
                    if (target.hasClass('tagit-label')) {
                        that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
                    } else {
                        // Sets the focus() to the input field, if the user
                        // clicks anywhere inside the UL. This is needed
                        // because the input field needs to be of a small size.
                        that.tagInput.focus();
                    }
                });

            // Single field support.
            var addedExistingFromSingleFieldNode = false;
            if (this.options.singleField) {
                if (this.options.singleFieldNode) {
                    // Add existing tags from the input field.
                    var node = $(this.options.singleFieldNode);
                    var tags = node.val().split(this.options.singleFieldDelimiter);
                    node.val('');
                    $.each(tags, function(index, tag) {
                        that.createTag(tag);
                        addedExistingFromSingleFieldNode = true;
                    });
                } else {
                    // Create our single field input after our list.
                    this.options.singleFieldNode = $('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
                    this.tagList.after(this.options.singleFieldNode);
                }
            }

            // Add existing tags from the list, if any.
            if (!addedExistingFromSingleFieldNode) {
                this.tagList.children('li').each(function() {
                    if (!$(this).hasClass('tagit-new')) {
                        that.createTag($(this).text(), $(this).attr('class'));
                        $(this).remove();
                    }
                });
            }

            // Events.
            this.tagInput
                .keydown(function(event) {
                    // Backspace is not detected within a keypress, so it must use keydown.
                    if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') {
                        var tag = that._lastTag();
                        if (!that.options.removeConfirmation || tag.hasClass('remove')) {
                            // When backspace is pressed, the last tag is deleted.
                            that.removeTag(tag);
                        } else if (that.options.removeConfirmation) {
                            tag.addClass('remove ui-state-highlight');
                        }
                    } else if (that.options.removeConfirmation) {
                        that._lastTag().removeClass('remove ui-state-highlight');
                    }

                    // Comma/Space/Enter are all valid delimiters for new tags,
                    // except when there is an open quote or if setting allowSpaces = true.
                    // Tab will also create a tag, unless the tag input is empty,
                    // in which case it isn't caught.
                    if (
                        event.which === $.ui.keyCode.COMMA ||
                        event.which === $.ui.keyCode.ENTER ||
                        (
                            event.which == $.ui.keyCode.TAB &&
                            that.tagInput.val() !== ''
                        ) ||
                        (
                            event.which == $.ui.keyCode.SPACE &&
                            that.options.allowSpaces !== true &&
                            (
                                $.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
                                (
                                    $.trim(that.tagInput.val()).charAt(0) == '"' &&
                                    $.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' &&
                                    $.trim(that.tagInput.val()).length - 1 !== 0
                                )
                            )
                        )
                    ) {
                        // Enter submits the form if there's no text in the input.
                        if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) {
                            event.preventDefault();
                        }

                        that.createTag(that._cleanedInput());

                        // The autocomplete doesn't close automatically when TAB is pressed.
                        // So let's ensure that it closes.
                        that.tagInput.autocomplete('close');
                    }
                }).blur(function(e){
                    // Create a tag when the element loses focus (unless it's empty).
                    that.createTag(that._cleanedInput());
                });

            // Autocomplete.
            if (this.options.availableTags || this.options.tagSource) {
                this.tagInput.autocomplete({
                    source: this.options.tagSource,
                    select: function(event, ui) {
                        // Delete the last tag if we autocomplete something despite the input being empty
                        // This happens because the input's blur event causes the tag to be created when
                        // the user clicks an autocomplete item.
                        // The only artifact of this is that while the user holds down the mouse button
                        // on the selected autocomplete item, a tag is shown with the pre-autocompleted text,
                        // and is changed to the autocompleted text upon mouseup.
                        if (that.tagInput.val() === '') {
                            that.removeTag(that._lastTag(), false);
                        }
                        that.createTag(ui.item.value);
                        // Preventing the tag input to be updated with the chosen value.
                        return false;
                    }
                });
            }
        },

        _cleanedInput: function() {
            // Returns the contents of the tag input, cleaned and ready to be passed to createTag
            return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1'));
        },

        _lastTag: function() {
            return this.tagList.children('.tagit-choice:last');
        },

        _tags: function() {
            return this.tagList.children('.tagit-choice');
        },

        assignedTags: function() {
            // Returns an array of tag string values
            var that = this;
            var tags = [];
            if (this.options.singleField) {
                tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
                if (tags[0] === '') {
                    tags = [];
                }
            } else {
                this._tags().each(function() {
                    tags.push(that.tagLabel(this));
                });
            }
            return tags;
        },

        _updateSingleTagsField: function(tags) {
            // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
            $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change');
        },

        _subtractArray: function(a1, a2) {
            var result = [];
            for (var i = 0; i < a1.length; i++) {
                if ($.inArray(a1[i], a2) == -1) {
                    result.push(a1[i]);
                }
            }
            return result;
        },

        tagLabel: function(tag) {
            // Returns the tag's string label.
            if (this.options.singleField) {
                return $(tag).children('.tagit-label').text();
            } else {
                return $(tag).children('input').val();
            }
        },

        _isNew: function(value) {
            var that = this;
            var isNew = true;
            this._tags().each(function(i) {
                if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
                    isNew = false;
                    return false;
                }
            });
            return isNew;
        },

        _formatStr: function(str) {
            if (this.options.caseSensitive) {
                return str;
            }
            return $.trim(str.toLowerCase());
        },

        createTag: function(value, additionalClass) {
            var that = this;

            value = $.trim(value);

            if (!this.allowDuplicates && (!this._isNew(value) || value === '')) {
                return false;
            }

            var label = $(this.options.onTagClicked ? '' : '').text(value);

            // Create tag.
            var tag = $('

')
                .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
                .addClass(additionalClass)
                .append(label);

            // Button for removing the tag.
            var removeTagIcon = $('')
                .addClass('ui-icon ui-icon-close');
            var removeTag = $('\xd7') // \xd7 is an X
                .addClass('tagit-close')
                .append(removeTagIcon)
                .click(function(e) {
                    // Removes a tag when the little 'x' is clicked.
                    that.removeTag(tag);
                });
            tag.append(removeTag);

            // Unless options.singleField is set, each tag has a hidden input field inline.
            if (this.options.singleField) {
                var tags = this.assignedTags();
                tags.push(value);
                this._updateSingleTagsField(tags);
            } else {
                var escapedValue = label.html();
                tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]" />');
            }

            this._trigger('onTagAdded', null, tag);

            // Cleaning the input.
            this.tagInput.val('');

            // insert tag
            this.tagInput.parent().before(tag);
        },

        removeTag: function(tag, animate) {
            animate = typeof animate === "undefined" ? this.options.animate : animate;

            tag = $(tag);

            this._trigger('onTagRemoved', null, tag);

            if (this.options.singleField) {
                var tags = this.assignedTags();
                var removedTagLabel = this.tagLabel(tag);
                tags = $.grep(tags, function(el){
                    return el != removedTagLabel;
                });
                this._updateSingleTagsField(tags);
            }
            // Animate the removal.
            if (animate) {
                var hide_args = ($.effects && $.effects.blind) ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast'];

                hide_args.push(function() {
                    tag.remove();
                });

                tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue();
            } else {
                tag.remove();
            }
        },

        removeTagByName: function(tagName, animate) {
            var toRemove = this._tags().find("input[value='" + tagName + "']").closest('.tagit-choice');
            if (toRemove.length === 0) {
                throw "No such tag exists with the name '" + tagName + "'";
            }
            this.removeTag(toRemove, animate);
        },

        removeAll: function() {
            // Removes all tags.
            var that = this;
            this._tags().each(function(index, tag) {
                that.removeTag(tag, false);
            });
        }

    });

})(jQuery);

<commit_msg>prevent extra events from being fired when autocompleting<commit_after>/*
* jQuery UI Tag-it!
*
* @version v2.0 (06/2011)
*
* Copyright 2011, Levy Carneiro Jr.
* Released under the MIT license.
* http://aehlke.github.com/tag-it/LICENSE
*
* Homepage:
*   http://aehlke.github.com/tag-it/
*
* Authors:
*   Levy Carneiro Jr.
*   Martin Rehfeld
*   Tobias Schmidt
*   Skylar Challand
*   Alex Ehlke
*
* Maintainer:
*   Alex Ehlke - Twitter: @aehlke
*
* Dependencies:
*   jQuery v1.4+
*   jQuery UI v1.8+
*/
(function($) {

    $.widget('ui.tagit', {
        options: {
            fieldName         : 'tags',
            availableTags     : [],
            tagSource         : null,
            removeConfirmation: false,
            caseSensitive     : true,
            placeholderText   : null,
            allowDuplicates   : false,

            // When enabled, quotes are not neccesary
            // for inputting multi-word tags.
            allowSpaces: false,

            // Whether to animate tag removals or not.
            animate: true,

            // The below options are for using a single field instead of several
            // for our form values.
            //
            // When enabled, will use a single hidden field for the form,
            // rather than one per tag. It will delimit tags in the field
            // with singleFieldDelimiter.
            //
            // The easiest way to use singleField is to just instantiate tag-it
            // on an INPUT element, in which case singleField is automatically
            // set to true, and singleFieldNode is set to that element. This
            // way, you don't need to fiddle with these options.
            singleField: false,

            singleFieldDelimiter: ',',

            // Set this to an input DOM node to use an existing form field.
            // Any text in it will be erased on init. But it will be
            // populated with the text of tags as they are created,
            // delimited by singleFieldDelimiter.
            //
            // If this is not set, we create an input node for it,
            // with the name given in settings.fieldName.
            singleFieldNode: null,

            // Optionally set a tabindex attribute on the input that gets
            // created for tag-it.
            tabIndex: null,

            // Event callbacks.
            onTagAdded  : null,
            onTagRemoved: null,
            onTagClicked: null
        },

        _create: function() {
            // for handling static scoping inside callbacks
            var that = this;

            // There are 2 kinds of DOM nodes this widget can be instantiated on:
            //     1. UL, OL, or some element containing either of these.
            //     2. INPUT, in which case 'singleField' is overridden to true,
            //        a UL is created and the INPUT is hidden.
            if (this.element.is('input')) {
                this.tagList = $('

').insertAfter(this.element);
                this.options.singleField = true;
                this.options.singleFieldNode = this.element;
                this.element.css('display', 'none');
            } else {
                this.tagList = this.element.find('ul, ol').andSelf().last();
            }

            this.tagInput = $('<input type="text" />').addClass('ui-widget-content');
            if (this.options.tabIndex) {
                this.tagInput.attr('tabindex', this.options.tabIndex);
            }
            if (this.options.placeholderText) {
                this.tagInput.attr('placeholder', this.options.placeholderText);
            }

            this.options.tagSource = this.options.tagSource || function(search, showChoices) {
                var filter = search.term.toLowerCase();
                var choices = $.grep(this.options.availableTags, function(element) {
                    // Only match autocomplete options that begin with the search term.
                    // (Case insensitive.)
                    return (element.toLowerCase().indexOf(filter) === 0);
                });
                showChoices(this._subtractArray(choices, this.assignedTags()));
            };

            // Bind tagSource callback functions to this context.
            if ($.isFunction(this.options.tagSource)) {
                this.options.tagSource = $.proxy(this.options.tagSource, this);
            }

            this.tagList
                .addClass('tagit')
                .addClass('ui-widget ui-widget-content ui-corner-all')
                // Create the input field.
                .append($('

').append(this.tagInput))
                .click(function(e) {
                    var target = $(e.target);
                    if (target.hasClass('tagit-label')) {
                        that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
                    } else {
                        // Sets the focus() to the input field, if the user
                        // clicks anywhere inside the UL. This is needed
                        // because the input field needs to be of a small size.
                        that.tagInput.focus();
                    }
                });

            // Single field support.
            var addedExistingFromSingleFieldNode = false;
            if (this.options.singleField) {
                if (this.options.singleFieldNode) {
                    // Add existing tags from the input field.
                    var node = $(this.options.singleFieldNode);
                    var tags = node.val().split(this.options.singleFieldDelimiter);
                    node.val('');
                    $.each(tags, function(index, tag) {
                        that.createTag(tag);
                        addedExistingFromSingleFieldNode = true;
                    });
                } else {
                    // Create our single field input after our list.
                    this.options.singleFieldNode = $('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
                    this.tagList.after(this.options.singleFieldNode);
                }
            }

            // Add existing tags from the list, if any.
            if (!addedExistingFromSingleFieldNode) {
                this.tagList.children('li').each(function() {
                    if (!$(this).hasClass('tagit-new')) {
                        that.createTag($(this).text(), $(this).attr('class'));
                        $(this).remove();
                    }
                });
            }

            // Events.
            this.tagInput
                .keydown(function(event) {
                    // Backspace is not detected within a keypress, so it must use keydown.
                    if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') {
                        var tag = that._lastTag();
                        if (!that.options.removeConfirmation || tag.hasClass('remove')) {
                            // When backspace is pressed, the last tag is deleted.
                            that.removeTag(tag);
                        } else if (that.options.removeConfirmation) {
                            tag.addClass('remove ui-state-highlight');
                        }
                    } else if (that.options.removeConfirmation) {
                        that._lastTag().removeClass('remove ui-state-highlight');
                    }

                    // Comma/Space/Enter are all valid delimiters for new tags,
                    // except when there is an open quote or if setting allowSpaces = true.
                    // Tab will also create a tag, unless the tag input is empty,
                    // in which case it isn't caught.
                    if (
                        event.which === $.ui.keyCode.COMMA ||
                        event.which === $.ui.keyCode.ENTER ||
                        (
                            event.which == $.ui.keyCode.TAB &&
                            that.tagInput.val() !== ''
                        ) ||
                        (
                            event.which == $.ui.keyCode.SPACE &&
                            that.options.allowSpaces !== true &&
                            (
                                $.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
                                (
                                    $.trim(that.tagInput.val()).charAt(0) == '"' &&
                                    $.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' &&
                                    $.trim(that.tagInput.val()).length - 1 !== 0
                                )
                            )
                        )
                    ) {
                        // Enter submits the form if there's no text in the input.
                        if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) {
                            event.preventDefault();
                        }

                        that.createTag(that._cleanedInput());

                        // The autocomplete doesn't close automatically when TAB is pressed.
                        // So let's ensure that it closes.
                        that.tagInput.autocomplete('close');
                    }
                }).blur(function(e){
                    // Create a tag when the element loses focus.
                    // If autocomplete is enabled and suggestion was clicked, don't add it.
                    if (!that.tagInput.data('autocomplete-open')) {
                        that.createTag(that._cleanedInput());
                    }
                });

            // Autocomplete.
            if (this.options.availableTags || this.options.tagSource) {
                this.tagInput.autocomplete({
                    source: this.options.tagSource,
                    select: function(event, ui) {
                        that.createTag(ui.item.value);
                        // Preventing the tag input to be updated with the chosen value.
                        return false;
                    }
                }).bind('autocompleteopen', function(event, ui) {
                    that.tagInput.data('autocomplete-open', true);
                }).bind('autocompleteclose', function(event, ui) {
                    that.tagInput.data('autocomplete-open', false)
                });
            }
        },

        _cleanedInput: function() {
            // Returns the contents of the tag input, cleaned and ready to be passed to createTag
            return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1'));
        },

        _lastTag: function() {
            return this.tagList.children('.tagit-choice:last');
        },

        _tags: function() {
            return this.tagList.children('.tagit-choice');
        },

        assignedTags: function() {
            // Returns an array of tag string values
            var that = this;
            var tags = [];
            if (this.options.singleField) {
                tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
                if (tags[0] === '') {
                    tags = [];
                }
            } else {
                this._tags().each(function() {
                    tags.push(that.tagLabel(this));
                });
            }
            return tags;
        },

        _updateSingleTagsField: function(tags) {
            // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
            $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change');
        },

        _subtractArray: function(a1, a2) {
            var result = [];
            for (var i = 0; i < a1.length; i++) {
                if ($.inArray(a1[i], a2) == -1) {
                    result.push(a1[i]);
                }
            }
            return result;
        },

        tagLabel: function(tag) {
            // Returns the tag's string label.
            if (this.options.singleField) {
                return $(tag).children('.tagit-label').text();
            } else {
                return $(tag).children('input').val();
            }
        },

        _isNew: function(value) {
            var that = this;
            var isNew = true;
            this._tags().each(function(i) {
                if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
                    isNew = false;
                    return false;
                }
            });
            return isNew;
        },

        _formatStr: function(str) {
            if (this.options.caseSensitive) {
                return str;
            }
            return $.trim(str.toLowerCase());
        },

        createTag: function(value, additionalClass) {
            var that = this;

            value = $.trim(value);

            if (!this.allowDuplicates && (!this._isNew(value) || value === '')) {
                return false;
            }

            var label = $(this.options.onTagClicked ? '' : '').text(value);

            // Create tag.
            var tag = $('

')
                .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
                .addClass(additionalClass)
                .append(label);

            // Button for removing the tag.
            var removeTagIcon = $('')
                .addClass('ui-icon ui-icon-close');
            var removeTag = $('\xd7') // \xd7 is an X
                .addClass('tagit-close')
                .append(removeTagIcon)
                .click(function(e) {
                    // Removes a tag when the little 'x' is clicked.
                    that.removeTag(tag);
                });
            tag.append(removeTag);

            // Unless options.singleField is set, each tag has a hidden input field inline.
            if (this.options.singleField) {
                var tags = this.assignedTags();
                tags.push(value);
                this._updateSingleTagsField(tags);
            } else {
                var escapedValue = label.html();
                tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]" />');
            }

            this._trigger('onTagAdded', null, tag);

            // Cleaning the input.
            this.tagInput.val('');

            // insert tag
            this.tagInput.parent().before(tag);
        },

        removeTag: function(tag, animate) {
            animate = typeof animate === "undefined" ? this.options.animate : animate;

            tag = $(tag);

            this._trigger('onTagRemoved', null, tag);

            if (this.options.singleField) {
                var tags = this.assignedTags();
                var removedTagLabel = this.tagLabel(tag);
                tags = $.grep(tags, function(el){
                    return el != removedTagLabel;
                });
                this._updateSingleTagsField(tags);
            }
            // Animate the removal.
            if (animate) {
                var hide_args = ($.effects && $.effects.blind) ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast'];

                hide_args.push(function() {
                    tag.remove();
                });

                tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue();
            } else {
                tag.remove();
            }
        },

        removeTagByName: function(tagName, animate) {
            var toRemove = this._tags().find("input[value='" + tagName + "']").closest('.tagit-choice');
            if (toRemove.length === 0) {
                throw "No such tag exists with the name '" + tagName + "'";
            }
            this.removeTag(toRemove, animate);
        },

        removeAll: function() {
            // Removes all tags.
            var that = this;
            this._tags().each(function(index, tag) {
                that.removeTag(tag, false);
            });
        }

    });

})(jQuery);

<|endoftext|>


<commit_before>/**************************************************************
    CLASSES
**************************************************************/
/**
 * A board game with rank information.
 * @class
 * @param {int} id - A unique id.
 * @param {position} - The current position in the ranked list.
 */
var game = function(id, position) {
    this.id = id;
    this.position = position;
    this.wins = 0;
    this.losses = 0;
    this.locked = false;
    this.ranked = false;

    var differential = function() {
        return wins - losses;
    }
}
/**
 * ToString for a game.
 * @method
 */
game.prototype.toString = function() {
    return this.id;
}

/**
 * A collection of games.
 * @class
 * @param {array} list - An array of games.
 */
var games = function(list) {
    // private
    /*var*/ game_matchups = new matchups();

    var compareGames = function(game1, game2) {
        if (game1.position < game2.position)
            return -1;

        if (game1.position > game2.position)
            return 1;

        return 0;
    }

    var isFirstGameBetter = function(game1, game2) {
        // todo: get user input

        console.log('contest: ' + game1 + ' vs ' + game2);

        return game1.id < game2.id;
    }

    var logMatchup = function(winner, loser) {
        var match = new matchup(winner, loser);

        game_matchups.add(match);
    }

    var rankGames = function(games, gameIndex1, gameIndex2) {
        var isFirstBetter =
            isFirstGameBetter(games[gameIndex1], games[gameIndex2]);

        if(isFirstBetter)
        {
            games[gameIndex1].wins += 1;
            games[gameIndex2].losses += 1;
            logMatchup(games[gameIndex1].id, games[gameIndex2].id);
            games = reposition(games, gameIndex1, gameIndex2);
        }
        else
        {
            games[gameIndex1].losses += 1;
            games[gameIndex2].wins += 1;
            logMatchup(games[gameIndex2].id, games[gameIndex1].id);
            games = reposition(games, gameIndex2, gameIndex1);
        }

        // todo: check_for_locks

        return games;
    }

    var reposition = function(games, winnerIndex, loserIndex) {
        var winnerPosition = games[winnerIndex].position;
        var loserPosition = games[loserIndex].position;

        if(winnerPosition < loserPosition) {
            if(games[winnerIndex].differential() > 0)
            {
                // todo: winner.position increases by differential, as long as it does not move past any games that have been ranked above
            }

            if(games[loserIndex].differential() < 0)
            {
                // todo: loser.position decreases by differential, as long as it does not move past any games that have been ranked below
            }
        }
        else {
            // todo: Array.prototype.map() only supported in IE9+
            games.map(function(item){
                if(item.position >= loserPosition && item.position < winnerPosition)
                    item.position += 1;
            });

            games[winnerIndex].position = loserPosition;
        }

        return games;
    }

    // public
    this.list = list;

    this.contestAll = function() {
        if(this.list.length <= 1)
        {
            this.lockList();
            return;
        }

        for(var i = 0; i < this.list.length; i += 2)
        {
            this.list = rankGames(this.list, i, i + 1);
            //this.sortList();
        }

        console.log(game_matchups.toString());
    }

    this.lock = function(id) {
        var list_local = this.list;
        var game_local =  _.find(list_local, function(game) {return game.id == id});
        var index = _.indexOf(list_local, game_local);

        game_local.locked = true;
        list_local[index] = game_local;
        this.list = list_local;
    }

    this.lockList = function() {
        _.each(this.list, this.lock, this);
    }

    this.sortList = function() {
        this.list.sort(compareGames);
    }
}
/**
 * ToString for games.
 * @method
 */
games.prototype.toString = function () {
    return this.list.toString();
}

/**
 * A matchup of two games.
 * @class
 * @param {string} winner - The winner of the matchup.
 * @param {string} loser - The loser of the matchup.
 */
var matchup = function (winner, loser) {
    this.winner = winner;
    this.loser = loser;
}
/**
 * ToString for a matchup.
 * @method
 */
matchup.prototype.toString = function () {
    return this.winner + '>' + this.loser;
}

/**
 * A collection of matchups.
 * @class
 */
var matchups = function () {
    this.list = [];

    this.add = function (matchup) {
        this.list.push(matchup);
    }

    this.getAllRankedLower = function (id, includeId) {
        if (includeId == undefined)
            includeId = false;

        var self = this;

        var losers = self.list.map(function(item){
            if (item.winner == id)
                return item.loser;
        }).filter(function(item){
            return item;
        });

        if (losers.length == 0) {
            return includeId ? [id] : [];
        }
        else {
            var children = losers.map(function(item) {
                return self.getAllRankedLower(item, true);
            });

            losers = losers.concat(_.flatten(children));

            if(includeId)
                losers.push(id);

            return _.uniq(losers);
        }
    }

    this.getAllRankedHigher = function (id, includeId) {
        if (includeId == undefined)
            includeId = false;

        var self = this;

        var winners = self.list.map(function(item){
            if (item.loser == id)
                return item.winner;
        }).filter(function(item){
            return item;
        });

        if (winners.length == 0) {
            return includeId ? [id] : [];
        }
        else {
            var parents = winners.map(function(item) {
                return self.getAllRankedHigher(item, true);
            });

            winners = winners.concat(_.flatten(parents));

            if(includeId)
                winners.push(id);

            return _.uniq(winners);
        }
    }
}
/**
 * ToString for matchups.
 * @method
 */
matchups.prototype.toString = function() {
    return this.list.toString();
}

/**************************************************************
    METHODS
**************************************************************/
var getGames = function() {
    // todo: get user input

    var games = [];
    var i, j;

    for(i = 1, j = 10; i <= 10; i++, j--) {
        games.push(new game(i, j));
    }

    return games;
}

/**************************************************************
    MAIN
**************************************************************/
var games_array = getGames();
var games_object = new games(games_array);
games_object.toString();
games_object.sortList();
games_object.toString();
games_object.contestAll();<commit_msg>Implement locking<commit_after>/**************************************************************
    CLASSES
**************************************************************/
/**
 * A board game with rank information.
 * @class
 * @param {int} id - A unique id.
 * @param {position} - The current position in the ranked list.
 */
var game = function(id, position) {
    this.id = id;
    this.position = position;
    this.wins = 0;
    this.losses = 0;
    this.locked = false;
    this.ranked = false;

    var differential = function() {
        return wins - losses;
    }
}
/**
 * ToString for a game.
 * @method
 */
game.prototype.toString = function() {
    return this.id;
}

/**
 * A collection of games.
 * @class
 * @param {array} list - An array of games.
 */
var games = function(list) {
    // private
    /*var*/ game_matchups = new matchups();

    var compareGames = function(game1, game2) {
        if (game1.position < game2.position)
            return -1;

        if (game1.position > game2.position)
            return 1;

        return 0;
    }

    var isFirstGameBetter = function(game1, game2) {
        // todo: get user input

        console.log('contest: ' + game1 + ' vs ' + game2);

        return game1.id < game2.id;
    }

    var lock = function(games, index) {
        var gameToLock = games[index];
        gameToLock.locked = true;
        games[index] = gameToLock;
        return games;
    }

    var lockAll = function(games) {
        return _.each(games, function(element, index, list) {return lock(list, index);});
    }

    var lockCompletelySortedGames = function(games) {
        for(var i = 0; i <= games.length; i++)
        {
            var gamesRankedLowerCount = game_matchups.getAllRankedLower(games[i].id).length;
            var gamesUnlockedExcludingCurrentGameCount = unlockedList().length - 1;

            if(!games[i].locked && gamesRankedLowerCount == gamesUnlockedExcludingCurrentGameCount)
            {
                games = lock(games, i);
            }
            else
            {
                break;
            }
        }

        for(var i = games.length; i <= 0; i--)
        {
            var gamesRankedHigherCount = game_matchups.getAllRankedHigher(games[i].id).length;
            var gamesUnlockedExcludingCurrentGameCount = unlockedList().length - 1;

            if(!games[i].locked && gamesRankedHigherCount == gamesUnlockedExcludingCurrentGameCount)
            {
                games = lock(games, i);
            }
            else
            {
                break;
            }
        }

        return games;
    }

    var logMatchup = function(winner, loser) {
        var match = new matchup(winner, loser);

        game_matchups.add(match);
    }

    var rankGames = function(games, gameIndex1, gameIndex2) {
        var isFirstBetter = isFirstGameBetter(games[gameIndex1], games[gameIndex2]);

        if(isFirstBetter)
        {
            games[gameIndex1].wins += 1;
            games[gameIndex2].losses += 1;
            logMatchup(games[gameIndex1].id, games[gameIndex2].id);
            games = reposition(games, gameIndex1, gameIndex2);
        }
        else
        {
            games[gameIndex1].losses += 1;
            games[gameIndex2].wins += 1;
            logMatchup(games[gameIndex2].id, games[gameIndex1].id);
            games = reposition(games, gameIndex2, gameIndex1);
        }

        games = lockCompletelySortedGames(games);

        return games;
    }

    var reposition = function(games, winnerIndex, loserIndex) {
        var winnerPosition = games[winnerIndex].position;
        var loserPosition = games[loserIndex].position;

        if(winnerPosition < loserPosition) {
            if(games[winnerIndex].differential() > 0)
            {
                // todo: winner.position increases by differential, as long as it does not move past any games that are locked or that have been ranked above
            }

            if(games[loserIndex].differential() < 0)
            {
                // todo: loser.position decreases by differential, as long as it does not move past any games that are locked or that have been ranked below
            }
        }
        else {
            // todo: Array.prototype.map() only supported in IE9+
            games.map(function(item){
                if(item.position >= loserPosition && item.position < winnerPosition)
                    item.position += 1;
            });

            games[winnerIndex].position = loserPosition;
        }

        return games;
    }

    var unlockedList = function() {
        return _.filter(this.list, function(game) {return !game.locked});
    }

    // public
    this.list = list;

    this.contestAll = function() {
        if(this.list.length <= 1)
        {
            lockList();
            return;
        }

        for(var i = 0; i < this.list.length; i += 2)
        {
            // todo: eliminate redundant sorts
            this.list = rankGames(this.list, i, i + 1);
            //this.sortList();
        }

        console.log(game_matchups.toString());
    }

    this.sortList = function() {
        this.list.sort(compareGames);
    }
}
/**
 * ToString for games.
 * @method
 */
games.prototype.toString = function () {
    return this.list.toString();
}

/**
 * A matchup of two games.
 * @class
 * @param {string} winner - The winner of the matchup.
 * @param {string} loser - The loser of the matchup.
 */
var matchup = function (winner, loser) {
    this.winner = winner;
    this.loser = loser;
}
/**
 * ToString for a matchup.
 * @method
 */
matchup.prototype.toString = function () {
    return this.winner + '>' + this.loser;
}

/**
 * A collection of matchups.
 * @class
 */
var matchups = function () {
    this.list = [];

    this.add = function (matchup) {
        this.list.push(matchup);
    }

    this.getAllRankedLower = function (id, includeId) {
        if (includeId == undefined)
            includeId = false;

        var self = this;

        var losers = self.list.map(function(item){
            if (item.winner == id)
                return item.loser;
        }).filter(function(item){
            return item;
        });

        if (losers.length == 0) {
            return includeId ? [id] : [];
        }
        else {
            var children = losers.map(function(item) {
                return self.getAllRankedLower(item, true);
            });

            losers = losers.concat(_.flatten(children));

            if(includeId)
                losers.push(id);

            return _.uniq(losers);
        }
    }

    this.getAllRankedHigher = function (id, includeId) {
        if (includeId == undefined)
            includeId = false;

        var self = this;

        var winners = self.list.map(function(item){
            if (item.loser == id)
                return item.winner;
        }).filter(function(item){
            return item;
        });

        if (winners.length == 0) {
            return includeId ? [id] : [];
        }
        else {
            var parents = winners.map(function(item) {
                return self.getAllRankedHigher(item, true);
            });

            winners = winners.concat(_.flatten(parents));

            if(includeId)
                winners.push(id);

            return _.uniq(winners);
        }
    }
}
/**
 * ToString for matchups.
 * @method
 */
matchups.prototype.toString = function() {
    return this.list.toString();
}

/**************************************************************
    METHODS
**************************************************************/
var getGames = function() {
    // todo: get user input

    var games = [];
    var i, j;

    for(i = 1, j = 10; i <= 10; i++, j--) {
        games.push(new game(i, j));
    }

    return games;
}

/**************************************************************
    MAIN
**************************************************************/
var games_array = getGames();
var games_object = new games(games_array);
games_object.toString();
games_object.sortList();
games_object.toString();
games_object.contestAll();<|endoftext|>