blob: cbd86be1a5c3857a6b2b1edad25da28efa14ffbf [file] [log] [blame]
// Copyright (C) 2010 Ojan Vafai. All rights reserved.
// Copyright (C) 2010 Adam Barth. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
WebKitCommitters = (function() {
var COMMITTERS_URL = 'https://svn.webkit.org/repository/webkit/trunk/metadata/contributors.json';
var m_committers;
function statusToType(status) {
if (status === 'reviewer')
return 'r';
if (status === 'committer')
return 'c';
return undefined;
}
function parseCommittersPy(text) {
m_committers = [];
JSON.parse(text).forEach(contributor => {
m_committers.push({
name: contributor.name,
emails: contributor.emails,
irc: contributor.nicks,
type: statusToType(contributor.status),
});
})
}
function loadCommitters(callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', COMMITTERS_URL);
xhr.onload = function() {
parseCommittersPy(xhr.responseText);
callback();
};
xhr.onerror = function() {
console.log('Unable to load contributors.json');
callback();
};
xhr.send();
}
function getCommitters(callback) {
if (m_committers) {
callback(m_committers);
return;
}
loadCommitters(function() {
callback(m_committers);
});
}
return {
"getCommitters": getCommitters
};
})();
(function() {
var SINGLE_EMAIL_INPUTS = ['email1', 'email2', 'email3', 'requester', 'requestee', 'assigned_to'];
var EMAIL_INPUTS = SINGLE_EMAIL_INPUTS.concat(['cc', 'newcc', 'new_watchedusers']);
var m_menus = {};
var m_focusedInput;
var m_committers;
var m_prefix;
var m_selectedIndex;
function contactsMatching(prefix) {
var list = [];
if (!prefix)
return list;
for (var i = 0; i < m_committers.length; i++) {
if (isMatch(m_committers[i], prefix))
list.push(m_committers[i]);
}
return list;
}
function startsWith(str, prefix) {
return str.toLowerCase().indexOf(prefix.toLowerCase()) == 0;
}
function startsWithAny(arry, prefix) {
for (var i = 0; i < arry.length; i++) {
if (startsWith(arry[i], prefix))
return true;
}
return false;
}
function isMatch(contact, prefix) {
if (startsWithAny(contact.emails, prefix))
return true;
if (contact.irc && startsWithAny(contact.irc, prefix))
return true;
var names = contact.name.split(' ');
for (var i = 0; i < names.length; i++) {
if (startsWith(names[i], prefix))
return true;
}
return false;
}
function isMenuVisible() {
return getMenu().style.display != 'none';
}
function showMenu(shouldShow) {
getMenu().style.display = shouldShow ? '' : 'none';
}
function updateMenu() {
var newPrefix = m_focusedInput.value;
if (newPrefix) {
newPrefix = newPrefix.slice(getStart(), getEnd());
newPrefix = newPrefix.replace(/^\s+/, '');
newPrefix = newPrefix.replace(/\s+$/, '');
}
if (m_prefix == newPrefix)
return;
m_prefix = newPrefix;
var contacts = contactsMatching(m_prefix);
if (contacts.length == 0 || contacts.length == 1 && contacts[0].emails[0] == m_prefix) {
showMenu(false);
return;
}
var html = [];
for (var i = 0; i < contacts.length; i++) {
var contact = contacts[i];
html.push('<div style="padding:1px 2px;" ' + 'email=' +
contact.emails[0] + '>' + contact.name + ' - ' + contact.emails[0]);
if (contact.irc)
html.push(' (:' + contact.irc + ')');
if (contact.type)
html.push(' (' + contact.type + ')');
html.push('</div>');
}
getMenu().innerHTML = html.join('');
selectItem(0);
showMenu(true);
}
function getIndex(item) {
for (var i = 0; i < getMenu().childNodes.length; i++) {
if (item == getMenu().childNodes[i])
return i;
}
console.error("Couldn't find item.");
}
function getMenu() {
return m_menus[m_focusedInput.name];
}
function createMenu(name, input) {
if (!m_menus[name]) {
var menu = document.createElement('div');
menu.style.cssText =
"position:absolute;border:1px solid black;background-color:white;-webkit-box-shadow:3px 3px 3px #888;";
menu.style.minWidth = m_focusedInput.offsetWidth + 'px';
m_focusedInput.parentNode.insertBefore(menu, m_focusedInput.nextSibling);
menu.addEventListener('mousedown', function(e) {
selectItem(getIndex(e.target));
e.preventDefault();
}, false);
menu.addEventListener('mouseup', function(e) {
if (m_selectedIndex == getIndex(e.target))
insertSelectedItem();
}, false);
m_menus[name] = menu;
}
}
function getStart() {
var index = m_focusedInput.value.lastIndexOf(',', m_focusedInput.selectionStart - 1);
if (index == -1)
return 0;
return index + 1;
}
function getEnd() {
var index = m_focusedInput.value.indexOf(',', m_focusedInput.selectionStart);
if (index == -1)
return m_focusedInput.value.length;
return index;
}
function getItem(index) {
return getMenu().childNodes[index];
}
function selectItem(index) {
if (index < 0 || index >= getMenu().childNodes.length)
return;
if (m_selectedIndex != undefined) {
getItem(m_selectedIndex).style.backgroundColor = '';
getItem(m_selectedIndex).style.color = '';
}
getItem(index).style.backgroundColor = '#039';
getItem(index).style.color = 'white';
m_selectedIndex = index;
}
function insertSelectedItem() {
var selectedEmail = getItem(m_selectedIndex).getAttribute('email');
var oldValue = m_focusedInput.value;
var newValue = oldValue.slice(0, getStart()) + selectedEmail + oldValue.slice(getEnd());
if (SINGLE_EMAIL_INPUTS.indexOf(m_focusedInput.name) == -1 &&
newValue.charAt(newValue.length - 1) != ',')
newValue = newValue + ',';
m_focusedInput.value = newValue;
showMenu(false);
}
function handleKeyDown(e) {
if (!isMenuVisible())
return;
switch (e.keyIdentifier) {
case 'Up':
selectItem(m_selectedIndex - 1);
e.preventDefault();
break;
case 'Down':
selectItem(m_selectedIndex + 1);
e.preventDefault();
break;
case 'Enter':
insertSelectedItem();
e.preventDefault();
break;
}
}
function handleKeyUp(e) {
if (e.keyIdentifier == 'Enter')
return;
if (m_focusedInput.selectionStart == m_focusedInput.selectionEnd)
updateMenu();
else
showMenu(false);
}
function enableAutoComplete(input) {
m_focusedInput = input;
if (!getMenu()) {
createMenu(m_focusedInput.name);
// Turn off autocomplete to avoid showing the browser's dropdown menu.
m_focusedInput.setAttribute('autocomplete', 'off');
m_focusedInput.addEventListener('keyup', handleKeyUp, false);
m_focusedInput.addEventListener('keydown', handleKeyDown, false);
m_focusedInput.addEventListener('blur', function() {
showMenu(false);
m_prefix = null;
m_selectedIndex = 0;
}, false);
// Turn on autocomplete on submit to avoid breaking autofill on back/forward navigation.
m_focusedInput.form.addEventListener("submit", function() {
m_focusedInput.setAttribute("autocomplete", "on");
}, false);
}
updateMenu();
}
for (var i = 0; i < EMAIL_INPUTS.length; i++) {
var field = document.getElementsByName(EMAIL_INPUTS[i])[0];
if (field)
field.addEventListener("focus", function(e) { enableAutoComplete(e.target); }, false);
}
WebKitCommitters.getCommitters(function (committers) {
m_committers = committers;
});
})();