// 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/Tools/Scripts/webkitpy/common/config/contributors.json'; var m_committers; function statusToType(status) { if (status === 'reviewer') return 'r'; if (status === 'committer') return 'c'; return undefined; } function parseCommittersPy(text) { var contributors = JSON.parse(text); m_committers = []; for (var name in contributors) { var record = contributors[name]; m_committers.push({ name: name, emails: record.emails, irc: record.nicks, type: statusToType(record.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('
' + contact.name + ' - ' + contact.emails[0]); if (contact.irc) html.push(' (:' + contact.irc + ')'); if (contact.type) html.push(' (' + contact.type + ')'); html.push('
'); } 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; }); })();