В то время как я соглашаюсь, что постоянная потребность отсортировать карту является, вероятно, запахом, я думаю, что следующий код является самым легким способом сделать это, не используя различную структуру данных.
public class MapUtilities {
public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
Collections.sort(entries, new ByValue<K, V>());
return entries;
}
private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
}
И вот смущающе неполный модульный тест:
public class MapUtilitiesTest extends TestCase {
public void testSorting() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("One", 1);
map.put("Two", 2);
map.put("Three", 3);
List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
assertEquals("First", "One", sorted.get(0).getKey());
assertEquals("Second", "Two", sorted.get(1).getKey());
assertEquals("Third", "Three", sorted.get(2).getKey());
}
}
результатом является отсортированный список Карты. Объекты записи, из которых можно получить ключи и значения.
Это совместимо со стандартными браузерами, но, вероятно, не будет работать в IE. Я предлагаю это как отправную точку. IE не поддерживает диапазон DOM.
var editable = document.getElementById('editable'),
selection, range;
// Populates selection and range variables
var captureSelection = function(e) {
// Don't capture selection outside editable region
var isOrContainsAnchor = false,
isOrContainsFocus = false,
sel = window.getSelection(),
parentAnchor = sel.anchorNode,
parentFocus = sel.focusNode;
while(parentAnchor && parentAnchor != document.documentElement) {
if(parentAnchor == editable) {
isOrContainsAnchor = true;
}
parentAnchor = parentAnchor.parentNode;
}
while(parentFocus && parentFocus != document.documentElement) {
if(parentFocus == editable) {
isOrContainsFocus = true;
}
parentFocus = parentFocus.parentNode;
}
if(!isOrContainsAnchor || !isOrContainsFocus) {
return;
}
selection = window.getSelection();
// Get range (standards)
if(selection.getRangeAt !== undefined) {
range = selection.getRangeAt(0);
// Get range (Safari 2)
} else if(
document.createRange &&
selection.anchorNode &&
selection.anchorOffset &&
selection.focusNode &&
selection.focusOffset
) {
range = document.createRange();
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
} else {
// Failure here, not handled by the rest of the script.
// Probably IE or some older browser
}
};
// Recalculate selection while typing
editable.onkeyup = captureSelection;
// Recalculate selection after clicking/drag-selecting
editable.onmousedown = function(e) {
editable.className = editable.className + ' selecting';
};
document.onmouseup = function(e) {
if(editable.className.match(/\sselecting(\s|$)/)) {
editable.className = editable.className.replace(/ selecting(\s|$)/, '');
captureSelection();
}
};
editable.onblur = function(e) {
var cursorStart = document.createElement('span'),
collapsed = !!range.collapsed;
cursorStart.id = 'cursorStart';
cursorStart.appendChild(document.createTextNode('—'));
// Insert beginning cursor marker
range.insertNode(cursorStart);
// Insert end cursor marker if any text is selected
if(!collapsed) {
var cursorEnd = document.createElement('span');
cursorEnd.id = 'cursorEnd';
range.collapse();
range.insertNode(cursorEnd);
}
};
// Add callbacks to afterFocus to be called after cursor is replaced
// if you like, this would be useful for styling buttons and so on
var afterFocus = [];
editable.onfocus = function(e) {
// Slight delay will avoid the initial selection
// (at start or of contents depending on browser) being mistaken
setTimeout(function() {
var cursorStart = document.getElementById('cursorStart'),
cursorEnd = document.getElementById('cursorEnd');
// Don't do anything if user is creating a new selection
if(editable.className.match(/\sselecting(\s|$)/)) {
if(cursorStart) {
cursorStart.parentNode.removeChild(cursorStart);
}
if(cursorEnd) {
cursorEnd.parentNode.removeChild(cursorEnd);
}
} else if(cursorStart) {
captureSelection();
var range = document.createRange();
if(cursorEnd) {
range.setStartAfter(cursorStart);
range.setEndBefore(cursorEnd);
// Delete cursor markers
cursorStart.parentNode.removeChild(cursorStart);
cursorEnd.parentNode.removeChild(cursorEnd);
// Select range
selection.removeAllRanges();
selection.addRange(range);
} else {
range.selectNode(cursorStart);
// Select range
selection.removeAllRanges();
selection.addRange(range);
// Delete cursor marker
document.execCommand('delete', false, null);
}
}
// Call callbacks here
for(var i = 0; i < afterFocus.length; i++) {
afterFocus[i]();
}
afterFocus = [];
// Register selection again
captureSelection();
}, 10);
};
Обновление
Я написал кроссбраузерную библиотеку диапазонов и выбора под названием Rangy , которая включает улучшенную версию кода, который я разместил ниже. Вы можете использовать модуль сохранения и восстановления выделения для этого конкретного вопроса, хотя я бы хотел использовать что-то вроде ответа @Nico Burns , если вы больше ничего не делаете с выделениями в вашем проекте и вам не нужна большая часть библиотеки.
Предыдущий ответ
Вы можете использовать IERange ( http://code.google.com/p/ierange/ ) для преобразования IE TextRange в нечто вроде DOM Range и используйте его в сочетании с чем-то вроде отправной точки без век. Лично я бы использовал только алгоритмы из IERange, которые выполняют Range <-> TextRange, а не использовать все это целиком. И объект выбора IE не имеет свойств focusNode и anchorNode, но вместо этого вы должны иметь возможность просто использовать Range / TextRange, полученный из выбора.
Я мог бы собрать что-то вместе, чтобы сделать это, отправлю сюда, если и когда Да.
РЕДАКТИРОВАТЬ:
Я создал демонстрацию скрипта, который делает это. Он работает во всем, что я пробовал, за исключением ошибки в Opera 9, которую я еще не успел изучить. Он работает в браузерах IE 5.5, 6 и 7, Chrome 2, Firefox 2, 3 и 3.5 и Safari 4, все в Windows
http://www.timdown.co. uk / code / selections /
Обратите внимание, что выбор может быть сделан в обратном направлении в браузерах, чтобы узел фокуса находился в начале выделения, а нажатие правой или левой клавиши курсора переместит курсор в положение относительно начала выбор. Я не думаю, что возможно воспроизвести это при восстановлении выделения, поэтому узел фокуса всегда находится в конце выделения.
Я напишу это полностью в какой-то момент в ближайшее время.
Это решение работает во всех основных браузерах:
saveSelection()
прикрепляется к событиям onmouseup
и onkeyup
div и сохраняет выбор в переменную savedRange
.
restoreSelection()
прикрепляется к событию onfocus
div и повторно выбирает выбор, сохраненный в savedRange
.
Это работает идеально, если только вы не хотите, чтобы выбор восстанавливался, когда пользователь щелкает по div, а также (что немного неинтуитивно, поскольку обычно вы ожидаете, что курсор будет перемещаться туда, куда вы щелкаете, но код включен для полноты)
Для достижения этого события onclick
и onmousedown
отменяются функцией cancelEvent()
, которая является кроссбраузерной функцией для отмены события. Функция cancelEvent()
также запускает функцию restoreSelection()
, так как при отмене события click div не получает фокус, и поэтому ничего не выбирается, пока не будет запущена эта функция.
Переменная isInFocus
хранит информацию о том, находится ли div в фокусе, и изменяется на "false" onblur
и "true" onfocus
. Это позволяет отменять события щелчка только в том случае, если div не находится в фокусе (иначе вы вообще не смогли бы изменить выделение).
Если вы хотите, чтобы выделение изменялось, когда div фокусируется по щелчку, и не восстанавливало выделение onclick
(и только когда фокус задается элементу программно с помощью document.getElementById("area").focus();
или подобным образом, то просто удалите события onclick
и onmousedown
. Событие onblur
и функции onDivBlur()
и cancelEvent()
также могут быть удалены в этих обстоятельствах.
Этот код должен работать, если его вставить непосредственно в тело html-страницы, если вы хотите быстро протестировать его:
<div id="area" style="width:300px;height:300px;" onblur="onDivBlur();" onmousedown="return cancelEvent(event);" onclick="return cancelEvent(event);" contentEditable="true" onmouseup="saveSelection();" onkeyup="saveSelection();" onfocus="restoreSelection();"></div>
<script type="text/javascript">
var savedRange,isInFocus;
function saveSelection()
{
if(window.getSelection)//non IE Browsers
{
savedRange = window.getSelection().getRangeAt(0);
}
else if(document.selection)//IE
{
savedRange = document.selection.createRange();
}
}
function restoreSelection()
{
isInFocus = true;
document.getElementById("area").focus();
if (savedRange != null) {
if (window.getSelection)//non IE and there is already a selection
{
var s = window.getSelection();
if (s.rangeCount > 0)
s.removeAllRanges();
s.addRange(savedRange);
}
else if (document.createRange)//non IE and no selection
{
window.getSelection().addRange(savedRange);
}
else if (document.selection)//IE
{
savedRange.select();
}
}
}
//this part onwards is only needed if you want to restore selection onclick
var isInFocus = false;
function onDivBlur()
{
isInFocus = false;
}
function cancelEvent(e)
{
if (isInFocus == false && savedRange != null) {
if (e && e.preventDefault) {
//alert("FF");
e.stopPropagation(); // DOM style (return false doesn't always work in FF)
e.preventDefault();
}
else {
window.event.cancelBubble = true;//IE stopPropagation
}
restoreSelection();
return false; // false = IE style
}
}
</script>