|
/** |
|
* IGNORE THESE PROPERTIES, JUST SCROLL DOWN |
|
*/ |
|
(function(){ |
|
|
|
Object.defineProperty(HTMLPreElement.prototype, 'selectionStart', { |
|
get: function() { |
|
var selection = getSelection(); |
|
|
|
if(selection.rangeCount) { |
|
var range = selection.getRangeAt(0), |
|
element = range.startContainer, |
|
container = element, |
|
offset = range.startOffset; |
|
|
|
if(!(this.compareDocumentPosition(element) & 0x10)) { |
|
return 0; |
|
} |
|
|
|
do { |
|
while(element = element.previousSibling) { |
|
if(element.textContent) { |
|
offset += element.textContent.length; |
|
} |
|
} |
|
|
|
element = container = container.parentNode; |
|
} while(element && element != this); |
|
|
|
return offset; |
|
} |
|
else { |
|
return 0; |
|
} |
|
}, |
|
|
|
enumerable: true, |
|
configurable: true |
|
}); |
|
|
|
Object.defineProperty(HTMLPreElement.prototype, 'selectionEnd', { |
|
get: function() { |
|
var selection = getSelection(); |
|
|
|
if(selection.rangeCount) { |
|
return this.selectionStart + (selection.getRangeAt(0) + '').length; |
|
} |
|
else { |
|
return 0; |
|
} |
|
}, |
|
|
|
enumerable: true, |
|
configurable: true |
|
}); |
|
|
|
Object.defineProperty(HTMLPreElement.prototype, 'text', { |
|
get: function (el) { |
|
var node; |
|
var el = el || this; |
|
var nodeType = el.nodeType; |
|
var i = 0; |
|
var text = ''; |
|
|
|
// ELEMENT_NODE || DOCUMENT_NODE || DOCUMENT_FRAGMENT_NODE |
|
if (nodeType === 1 || nodeType === 9 || nodeType === 11) { |
|
if (typeof el.textContent === 'string') { |
|
return el.textContent; |
|
} |
|
else { |
|
// textContent can be null, in which case we walk the element tree |
|
for (el = el.firstChild; el; el = el.nextSibling) { |
|
text += _getText(el); |
|
} |
|
} |
|
} |
|
else if (nodeType === 3 || nodeType === 4) { |
|
return el.nodeValue; |
|
} |
|
else { |
|
for (; (node = el[i]); i++) { |
|
text += _getText(node); |
|
} |
|
} |
|
|
|
return text; |
|
}, |
|
|
|
enumerable: true, |
|
configurable: true |
|
}); |
|
|
|
HTMLPreElement.prototype.setSelectionRange = function(ss, se) { |
|
var range = document.createRange(), |
|
offset = findOffset(this, ss); |
|
|
|
range.setStart(offset.element, offset.offset); |
|
|
|
if(se && se != ss) { |
|
offset = findOffset(this, se); |
|
} |
|
|
|
range.setEnd(offset.element, offset.offset); |
|
|
|
var selection = window.getSelection(); |
|
selection.removeAllRanges(); |
|
selection.addRange(range); |
|
} |
|
|
|
function findOffset(root, ss) { |
|
if(!root) { |
|
return null; |
|
} |
|
|
|
var offset = 0, |
|
element = root; |
|
|
|
do { |
|
var container = element; |
|
element = element.firstChild; |
|
|
|
if(element) { |
|
do { |
|
var len = element.textContent.length; |
|
|
|
if(offset <= ss && offset + len > ss) { |
|
break; |
|
} |
|
|
|
offset += len; |
|
} while(element = element.nextSibling); |
|
} |
|
|
|
if(!element) { |
|
// It's the container's lastChild |
|
break; |
|
} |
|
} while(element && element.hasChildNodes() && element.nodeType != 3); |
|
|
|
if(element) { |
|
return { |
|
element: element, |
|
offset: ss - offset |
|
}; |
|
} |
|
else if(container) { |
|
element = container; |
|
|
|
while(element && element.lastChild) { |
|
element = element.lastChild; |
|
} |
|
|
|
if(element.nodeType === 3) { |
|
return { |
|
element: element, |
|
offset: element.textContent.length |
|
}; |
|
} |
|
else { |
|
return { |
|
element: element, |
|
offset: 0 |
|
}; |
|
} |
|
} |
|
|
|
return { |
|
element: root, |
|
offset: 0, |
|
error: true |
|
}; |
|
} |
|
|
|
}()); |
|
|
|
/** |
|
* IGNORE EVERYTHING ABOVE |
|
* START READING FROM HERE <==================================== |
|
* |
|
* Testing undo + [[Set]] text methods |
|
* |
|
* For most part, you can assume Chrome === Safari |
|
*/ |
|
|
|
/** |
|
* Microsoft API |
|
* |
|
* Chrome: undo works fine |
|
* Firefox: shit goes loose |
|
* Opera: ??? |
|
* IE8: ??? |
|
* IE9: ??? |
|
*/ |
|
HTMLPreElement.prototype.setInnerText = function(value) { |
|
console.log('setInnerText', this); |
|
this.innerText = value; |
|
}; |
|
|
|
/** |
|
* W3C API |
|
* |
|
* Chrome: undo does not work |
|
* Firefox: ??? |
|
* Opera: ??? |
|
* IE8: ??? |
|
* IE9: ??? |
|
*/ |
|
HTMLPreElement.prototype.setTextContent = function(value) { |
|
console.log('setTextContent', this); |
|
this.textContent = value; |
|
}; |
|
|
|
// Use TextNode but, empty() before appending |
|
/** |
|
* $.text style DOM manipulation |
|
* |
|
* Chrome: undo does not work |
|
* Firefox: ??? |
|
* Opera: ??? |
|
* IE8: ??? |
|
* IE9: ??? |
|
*/ |
|
HTMLPreElement.prototype.setTextNodeByReplacement = function(value) { |
|
console.log('setTextNodeByReplacement', this); |
|
var textNode = (this.ownerDocument || document).createTextNode(value); |
|
|
|
// Empty |
|
while (this.hasChildNodes()) { |
|
this.removeChild(this.lastChild); |
|
} |
|
|
|
// Append |
|
this.appendChild(textNode); |
|
}; |
|
|
|
/** |
|
* Similar to above, but instead of replacing we change the nodeValue |
|
* |
|
* Chrome: undo works |
|
* Firefox: ??? |
|
* Opera: ??? |
|
* IE8: ??? |
|
* IE9: ??? |
|
*/ |
|
HTMLPreElement.prototype.setTextNode = function(value) { |
|
console.log('setTextNode', this); |
|
var textNode = this.firstChild; |
|
textNode.nodeValue = value; |
|
}; |