User:V111P/js/valSel.js

/* valSel.js * ver. 2013-10 * * A cross-browser textarea and text input value and selection manipulation * library plug-in for jQuery * Home: http://en.wikipedia.org/wiki/User:V111P/js/valSel * * This script contains code from Rangy Text Inputs (Version from 5 November 2010), * Copyright 2010, Tim Down, licensed under the MIT license. * http://code.google.com/p/rangyinputs/ * * You can use the code not from Rangy Text Inputs under the CC0 license */

(function($) {	var UNDEF = "undefined";	var getSelection, setSelection, collapseSelection;	var inited; // whether init has already been called or not	var rReG = /\r/g;

// Trio of isHost* functions taken from Peter Michaux's article: // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting function isHostMethod(object, property) { var t = typeof object[property]; return t === "function" || (!!(t == "object" && object[property])) || t == "unknown"; }

function isHostProperty(object, property) { return typeof(object[property]) != UNDEF; }

function isHostObject(object, property) { return !!(typeof(object[property]) == "object" && object[property]); }

function fail(reason) { if (window.console && window.console.error) { window.console.error("valSel.js: Unsupported browser: " + reason); }	}

function adjustOffsets(el, start, end) { var len = el.value.replace(rReG, '').length; start = (start > 0 ? start : 0); start = (start < len ? start : len); if (typeof end == UNDEF) end = start; else { end = (end > 0 ? end : 0); end = (end < len ? end : len); }		if (end < start) { var t = start; start = end; end = t;		} return { start: start, end: end }; }

function makeSelection(normalizedValue, start, end) { return { start: start, end: end, length: end - start, text: normalizedValue.slice(start, end) };	}

function getBody { return isHostObject(document, "body") ? document.body : document.getElementsByTagName("body")[0]; }

function init { if (inited) return; var testTextArea = document.createElement("textarea");

getBody.appendChild(testTextArea);

if (isHostProperty(testTextArea, "selectionStart") && isHostProperty(testTextArea, "selectionEnd")) { getSelection = function(el) { var start = el.selectionStart, end = el.selectionEnd; return makeSelection(el.value.replace(rReG, ''), start, end); };

setSelection = function(el, startOffset, endOffset) { var offsets = adjustOffsets(el, startOffset, endOffset); el.selectionStart = offsets.start; el.selectionEnd = offsets.end; };

collapseSelection = function(el, toStart) { if (toStart) { el.selectionEnd = el.selectionStart; } else { el.selectionStart = el.selectionEnd; }			};		} else if (isHostMethod(testTextArea, "createTextRange") && isHostObject(document, "selection")		          && isHostMethod(document.selection, "createRange")) {

getSelection = function(el) { var start = 0, end = 0, range, normalizedValue = '', textInputRange, len, endRange;

el.focus; range = document.selection.createRange; if (range && range.parentElement == el) { len = el.value.length; normalizedValue = el.value.replace(rReG, ""); textInputRange = el.createTextRange; textInputRange.moveToBookmark(range.getBookmark); endRange = el.createTextRange; endRange.collapse(false); if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { start = end = len; } else { start = -textInputRange.moveStart("character", -len); if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { end = len; } else { end = -textInputRange.moveEnd("character", -len); }					}				}

return makeSelection(normalizedValue, start, end); };

// Moving across a line break only counts as moving one character in a TextRange, // whereas a line break in the textarea value is two characters. // This function corrects for that by converting a text offset into a range character offset // by subtracting one character for every line break in the textarea prior to the offset var offsetToRangeCharacterMove = function(el, offset) { return offset;// - (el.value.slice(0, offset).split("\r\n").length - 1); };

setSelection = function(el, startOffset, endOffset) { var offsets = adjustOffsets(el, startOffset, endOffset); var range = el.createTextRange; var startCharMove = offsetToRangeCharacterMove(el, offsets.start); range.collapse(true); if (offsets.start == offsets.end) { range.move("character", startCharMove); } else { range.moveEnd("character", offsetToRangeCharacterMove(el, offsets.end)); range.moveStart("character", startCharMove); }				range.select; };

collapseSelection = function(el, toStart) { var range = document.selection.createRange; range.collapse(toStart); range.select; };		} else { getBody.removeChild(testTextArea); fail("No means of finding text input caret position"); return; }

// Clean up		getBody.removeChild(testTextArea); inited = true; } // init

// the functions always automatically return this // if they don't otherwise return a result function jQuerify(func) { return function { var el = this.jquery ? this[0] : this; var nodeName = el.nodeName.toLowerCase;

if (el.nodeType == 1 && (nodeName == "textarea" || (nodeName == "input" && el.type == "text"))) { if (!inited) init; // because $(init) won't work after an error from another script var args = [el].concat(Array.prototype.slice.call(arguments)); var result = func.apply(this, args); if (typeof result != 'undefined') { return result; }			}			return this; };	}

/* Rangy Text Inputs code ends */

function getValue(el) { return el.value.replace(rReG, ''); // remove \r }

function setValue(el, val) { var scrollPosObj = scrollPos(el); el.value = val; scrollPos(el, scrollPosObj); }

function scrollPos(el, scrollPosObj) { if (scrollPosObj) { el.scrollTop = scrollPosObj.top; el.scrollLeft = scrollPosObj.left; }		else { return { top: el.scrollTop, left: el.scrollLeft };		}	}

var valParts = function (el, arg1, selText, textAfter) { var parts; var setValParts = function (el, parts) { setValue(el, parts.join('')); var beforeLen = parts[0].length; setSelection(el, beforeLen, beforeLen + parts[1].length); }

if (typeof arg1 == 'object') { parts = arg1; }		else if (typeof arg1 == 'function') { }		else if (typeof arg1 == 'string') { parts = [arg1, selText, textAfter]; }		if (parts) { setValParts(el, parts); return; }		else { var s = getSelection(el); var text = getValue(el);

parts = [text.slice(0, s.start), s.text, text.slice(s.end)];

if (typeof arg1 == 'function') { var parts = arg1(parts[0], parts[1], parts[2], text.length); if (parts) setValParts(el, parts); return; }

return parts; }	} // valParts

var sel = function (el, arg1, arg2) { var sel; var setSel = function (sel) { if (typeof sel.start == 'number') { if (typeof sel.end != 'number') sel.end = sel.start; if (typeof sel.text == 'string') setValue(sel.text); setSelection(el, sel.start, sel.end); }			else if (typeof sel.text == 'string') { valParts(el, function (pre, currSel, post) {					var replacementSel = sel.text;					if (collapse < 0) {						post = replacementSel + post;						replacementSel = ;					}					else if (collapse > 0) {						post += replacementSel;						replacementSel = ;					}

return [pre, replacementSel, post]; });			}		}

if (typeof arg1 == 'undefined') { return getSelection(el); }		switch (typeof arg1) { case 'function': var selObj = getSelection(el); sel = arg1(selObj.text, selObj.start, selObj.end, selObj.length); if (typeof sel == 'string') sel = {text: sel}; if (typeof sel == 'object') setSel(sel); break; case 'string': var collapse = (typeof arg2 == 'number' ? arg2 : 0); setSel({text: arg1, collapse: arg2}); break; case 'number': var start = arg1, end = arg2; setSelection(el, start, end); break; }	} // sel

var aroundSel = function (el) { var a = arguments; var before = a[1]; var after, include, collapse; var excludedBefore = , excludedAfter = ;

if (typeof before != 'string') return this; // error - do nothing if (typeof a[2] != 'string') { // aroundSel(string surround, [bool include, [int collapse]]) after = before; include = a[2]; collapse = a[3]; }		else if (typeof a[3] != 'string') { // aroundSel(string before, string after, [bool include, [int collapse]]) after = a[2]; include = a[3]; collapse = a[4]; }		else { // aroundSel(string before, string prepend, string append, string after, [int collapse]) excludedBefore = before; before = a[2]; after = a[3]; excludedAfter = a[4]; collapse = a[5]; include = true; }

include = (typeof include == 'boolean' ? include : true);

if (collapse === true) collapse = 1; else if (typeof collapse != 'number') collapse = 0;

valParts(el, function (pre, sel, post) {			if (include)				sel = before + sel + after;			else {				pre = pre + before;				post = after + post;			}

if (collapse < 0) { post = sel + post; sel = ''; }			else if (collapse > 0) { pre += sel; sel = ''; }

return [pre, sel, post]; });	} // aroundSel

$.fn.extend({		valParts: jQuerify(valParts),		sel: jQuerify(sel),		aroundSel: jQuerify(aroundSel),		collapseSel: jQuerify(collapseSelection)	});

$(init); })(jQuery);