|
(function() { |
|
[].forEach.call(document.querySelectorAll('.stretch'), function(el) { |
|
el.insertBefore(document.createElement('span'), el.firstChild); |
|
el.insertBefore(document.createElement('span'), null); |
|
}); |
|
|
|
document.addEventListener('dragstart', function(event) { |
|
event.preventDefault(); |
|
}, true); |
|
|
|
function isStretchHandle(node) { |
|
return node.parentNode.nodeType == 1 && node.parentNode.classList.contains('stretch') && node.nodeName == 'SPAN'; |
|
} |
|
|
|
var stretchTarget = null, |
|
stretchingFromEnd = false, |
|
mouseDownX = 0, |
|
mouseDownY = 0, |
|
stretching = false; |
|
|
|
function setBackwardSelection(selection, range) { |
|
if (!selection.extend) { |
|
// IE... |
|
selection.addRange(range); |
|
return; |
|
} |
|
|
|
var endRange = range.cloneRange(); |
|
endRange.collapse(false); |
|
selection.addRange(endRange); |
|
selection.extend(range.startContainer, range.startOffset); |
|
} |
|
|
|
function collapseAndPreserveRange(wrapper, range) { |
|
// Grab static copy of range properties |
|
var startContainer = range.startContainer, |
|
startOffset = range.startOffset, |
|
endContainer = range.endContainer, |
|
endOffset = range.endOffset; |
|
|
|
for (var child = wrapper.firstChild, offset = 0; child; child = wrapper.firstChild, ++offset) { |
|
wrapper.parentNode.insertBefore(child, wrapper); |
|
|
|
if (child === startContainer) { |
|
range.setStart(child, startOffset); |
|
} else if (wrapper === startContainer && offset === startOffset) { |
|
range.setStartBefore(wrapper); |
|
} |
|
|
|
if (child === endContainer) { |
|
range.setEnd(child, endOffset); |
|
} else if (wrapper === endContainer && offset === endOffset) { |
|
range.setEndBefore(wrapper); |
|
} |
|
} |
|
|
|
wrapper.parentNode.removeChild(wrapper); |
|
} |
|
|
|
function isValidRangeForRewrap(range, wrapper) { |
|
var startContainer = range.startContainer; |
|
if (wrapper.contains(startContainer)) { |
|
startContainer = wrapper.parentNode; |
|
} |
|
if (startContainer.nodeType !== 1) { |
|
startContainer = startContainer.parentNode; |
|
} |
|
|
|
var endContainer = range.endContainer; |
|
if (wrapper.contains(endContainer)) { |
|
endContainer = wrapper.parentNode; |
|
} |
|
if (endContainer.nodeType !== 1) { |
|
endContainer = endContainer.parentNode; |
|
} |
|
|
|
return startContainer === endContainer; |
|
} |
|
|
|
function rewrap(wrapper, range) { |
|
if (!isValidRangeForRewrap(range, wrapper)) { |
|
console.warn('Final range not valid for rewrap'); |
|
return; |
|
} |
|
|
|
var handles = [wrapper.firstChild, wrapper.lastChild]; |
|
|
|
collapseAndPreserveRange(wrapper, range); |
|
|
|
range.surroundContents(wrapper); |
|
wrapper.insertBefore(handles[0], wrapper.firstChild); |
|
wrapper.appendChild(handles[1]); |
|
} |
|
|
|
document.addEventListener('mousedown', function(event) { |
|
if (!isStretchHandle(event.target)) { |
|
return; |
|
} |
|
|
|
stretchTarget = event.target.parentNode; |
|
stretchingFromEnd = !event.target.nextSibling; |
|
|
|
mouseDownX = event.clientX; |
|
mouseDownY = event.clientY; |
|
document.body.classList.add('stretching'); |
|
}, true); |
|
|
|
document.addEventListener('mousemove', function(event) { |
|
if (!stretchTarget) { |
|
return; |
|
} |
|
|
|
var selection = window.getSelection(); |
|
|
|
// Wait for enough movement |
|
if (!stretching) { |
|
var dx = event.clientX - mouseDownX, |
|
dy = event.clientY - mouseDownY; |
|
if (dx * dx + dy * dy < 100) { |
|
return; |
|
} |
|
stretching = true; |
|
|
|
// Select the stretchable element |
|
var range = selection.rangeCount ? selection.getRangeAt(0) : document.createRange(); |
|
range.selectNode(stretchTarget); |
|
selection.removeAllRanges(); |
|
if (stretchingFromEnd) { |
|
selection.addRange(range); |
|
} else { |
|
setBackwardSelection(selection, range); |
|
} |
|
} |
|
|
|
document.body.classList.toggle('stretching-invalid', !selection.rangeCount || !isValidRangeForRewrap(selection.getRangeAt(0), stretchTarget)); |
|
}, true); |
|
|
|
document.addEventListener('mouseup', function(event) { |
|
if (!stretchTarget) { |
|
return; |
|
} |
|
|
|
document.body.classList.remove('stretching'); |
|
document.body.classList.remove('stretching-invalid'); |
|
|
|
var wrapper = stretchTarget; |
|
stretchTarget = null; |
|
|
|
if (!stretching) { |
|
return; |
|
} |
|
stretching = false; |
|
|
|
var selection = window.getSelection(); |
|
if (!selection.rangeCount) { |
|
console.warn('expected to find a range in the selection'); |
|
return; |
|
} |
|
|
|
var range = selection.getRangeAt(0); |
|
rewrap(wrapper, range); |
|
range.detach(); |
|
|
|
window.getSelection().removeAllRanges(); |
|
}, true); |
|
})(); |