{"id":2775,"date":"2013-02-08T05:08:50","date_gmt":"2013-02-08T11:08:50","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=2775"},"modified":"2015-11-11T14:52:09","modified_gmt":"2015-11-11T20:52:09","slug":"bililiterange-plugins","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2013\/02\/08\/bililiterange-plugins\/","title":{"rendered":"<code>bililiteRange<\/code> Plugins"},"content":{"rendered":"<p><strong>Edited 2014-02-27 to add the indentation code.<\/strong><\/p>\r\n<p>I created a collection of useful plugins for my <a href=\"http:\/\/bililite.com\/blog\/2011\/01\/17\/cross-browser-text-ranges-and-selections\/\" title=\"Cross-Browser Text Ranges and Selections\">bililiteRange<\/a>, for more sophisticated manipulations. Now you can search the element with a regular expression, and you can keep the range \"live\", adjusting it when the text is edited.<\/p>\r\n<p><a href=\"\/inc\/bililiteRange.util.js\">Download the code<\/a>.<\/p>\r\n<p><a href=\"\/blog\/blogfiles\/bililiteRange.util.html\">See the demo<\/a>. (The dynamic highlighting is off by a few pixels in iOS Safari. I'm still working on that)<\/p>\r\n<!--more-->\r\n<h3>The Methods<\/h3>\r\n<dl>\r\n<dt><code>bounds ('startbounds' | 'endbounds' | 'line' | 'BOL' | 'EOL' )<\/code><\/dt>\r\n<dd>(Other uses of <code>bounds<\/code> remain unchanged). Modifies the bounds of the range. <code class=\"language-javascript\" >bounds('startbounds')<\/code> and <code class=\"language-javascript\" >bounds('endbounds')<\/code> collapse the range to its boundaries.<\/dd>\r\n<dd><code class=\"language-javascript\" >'line'<\/code> changes the range to encompass the entire line that contains the <em>start<\/em> of the existing range. A line is defined as <code class=\"language-javascript\" >\/^.*$\/m<\/code>; that is, they are delimited by newlines. This works well for <code class=\"language-html\" >&lt;textarea&gt;<\/code>s but \r\n<code class=\"language-html\" >&lt;div contenteditable&gt;<\/code>s are flaky and inconsistent between browsers about whether newlines are added or \r\n<code class=\"language-html\" >&lt;br&gt;<\/code>s or new <code class=\"language-html\" >&lt;div&gt;<\/code>s are created. So use at your own risk.<\/dd>\r\n<dd><code class=\"language-javascript\" >bounds('BOL')<\/code> and <code class=\"language-javascript\" >bounds('EOL')<\/code> mean \"beginning of line\" and \"end of line\"; they move the bounds to the start or end of the line containing the start of the range; they act like\r\n <code class=\"language-javascript\" >rng.bounds('line').bounds('startbounds' or 'endbounds')<\/code>.<\/dd>\r\n\r\n<dt><code>find (re {Regular Expression}[, nowrap {Boolean} [, backwards {Boolean}]])<\/code><\/dt>\r\n<dd>Search for the next occurence of re in the entire text of the element from the start of the current bounds until the end and\r\n set the bounds to the found string. If\r\nnowrap is true, then the search will fail if it not found; otherwise the search continues from the start of the element.\r\n If backwards is true, then the search goes backwards from the start of the current bounds to the start of the element, possibly wrapping around to\r\nthe end of the element.<\/dd>\r\n<dd>If re is not found, the bounds remain the same. If the found string will never be identical to the current bounds; that allows for a \"Find Again\" functionality; range.find(\/foo\/).find(\/foo\/) finds the second occurrence of foo.<\/p>\r\n<dd>range.match is set to the result of <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/JavaScript\/Reference\/Global_Objects\/RegExp\/exec#Description\">re.exec<\/a>, so you can test for success or failure and get the captured groups. If you use <code class=\"language-javascript\" >emailRE = \/(\\S+)@(\\w+\\.\\w+)\/<\/code>\r\n as a primitive email matcher, then \r\n<pre><code class=\"language-javascript\" >for (rng = bililiteRange(element).find(emailRE); rng.match; rng.find(emailRE, true)){\r\n  alert('User: '+rng.match[1]+' Server: '+rng.match[2]);\r\n  rng.bounds('endbounds');\r\n}<\/code><\/pre>\r\n will find all email addresses and display them.<\/dd>\r\n\r\n<dt><code>findBack (re {Regular Expression}[, nowrap {Boolean}])<\/code><\/dt>\r\n<dd>Just a shorthand for <code class=\"language-javascript\" >find(re, nowrap, true)<\/code>.<\/dd>\r\n\r\n<dt><code>line()<\/code><\/dt>\r\n<dd>Return the line number (1-indexed!) of the line that contains the start of the range. Lines are defined as for <code class=\"language-javascript\" >bounds('line')<\/code> above.<\/dd>\r\n\r\n<dt><code>line(n)<\/code><\/dt>\r\n<dd>Set the range to line <code>n<\/code>, 1-indexed. It tries to emulate a \"down arrow\" by setting the range to the character position on the new line corresponding to the character position of the end of the current bounds. <code class=\"language-javascript\" >line(n).bounds('line')<\/code> will select the entire line. <code class=\"language-javascript\" >line(0)<\/code> is the same as <code class=\"language-javascript\" >bounds([0,0])<\/code> and if <code>n<\/code> is past the end of the text, it is the same as <code class=\"language-javascript\" >bounds('all').bounds('endbounds')<\/code>.<\/dd>\r\n\r\n<dt><code>live([{Boolean}])<\/code><\/dt>\r\n<dd><code class=\"language-javascript\" >range.live()<\/code> or <code class=\"language-javascript\" >range.live(true)<\/code> makes the range <em>live<\/em>; it listens for <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/DOM\/DOM_event_reference\/input\"><code>input<\/code> events<\/a> and attempts to update the bounds of the range so it still refers to the same text, they way bookmarks do in Microsoft Word. Unfortunately, <code>input<\/code> events do not include any information about <em>what<\/em> was changed, so the function compares the old and the new text and tries to figure out what was added. Usually that's straightforward, but pasting text that already exists may confuse it. Inserted text within or at the borders of a range expand the range; deleted text that includes part of the range shrinks it. If all the text of a range is deleted, the range is set to a zero-length range in at the start of the inserted text (ranges are never deleted).<\/dd>\r\n<dd>Since it relies on <code>input<\/code> events and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff975245(v=VS.85).aspx\"><code>addEventListener<\/code><\/a> it will fail in older versions of IE.<\/dd>\r\n\r\n<\/dl>\r\n\r\n<h3>Indentation<\/h3>\r\n<p>Some of the plugins add indentation (adding tabs or spaces at the beginning of a line). Whether you should use tabs or spaces is one of those <a href=\"http:\/\/c2.com\/cgi\/wiki?TabsVersusSpaces\">geek religious wars<\/a>, and this package is agnostic.<\/p>\r\n\r\n<dl>\r\n<dt><code>text(String, String, Boolean)<\/code><\/dt>\r\n<dd>If the last <code class=\"language-javascript\" >Boolean<\/code> is <code class=\"language-javascript\" >true<\/code>, then auto-indent the inserted <code class=\"language-javascript\" >String<\/code>: copy <code class=\"language-javascript\" >this.indentation()<\/code> after every newline character in the inserted text. The other uses of <code class=\"language-javascript\" >text()<\/code> and the other parameters remain the same.<\/dd>\r\n\r\n<dt><code>indentation()<\/code><\/dt>\r\n<dd>Returns the whitespace (anything that matches <code class=\"language-javascript\" >\/\\s*\/<\/code>) at the beginning of the line that contains the start of the range. Does not include any newline.<\/dd>\r\n\r\n<dt><code>indent(String)<\/code><\/dt>\r\n<dd>Inserts the <code class=\"language-javascript\" >String<\/code> at the beginning of every line of the range (including the line that the range starts on, even if the range does not include the start of the line).<\/dd>\r\n\r\n<dt><code>unindent(n {Number}, tabSize {Number})<\/code><\/dt>\r\n<dd>Removes <code class=\"language-javascript\" >n<\/code> \"tabs\" from the beginning of every line of the range (including the line that the range starts on, even if the range does not include the start of the line). A \"tab\" is either a <code class=\"language-javascript\" >'\\t'<\/code> or a sequence of <code class=\"language-javascript\" >tabSize<\/code> spaces. If <code class=\"language-javascript\" >tabSize<\/code> is not set, uses <code class=\"language-javascript\" >this.data().tabSize || 8<\/code>.<\/dd>\r\n\r\n<\/dl>\r\n\r\n<p>For a demo of autoindenting, see the <a href=\"\/blog\/blogfiles\/prism\/prismeditor.html\">Prism editor<\/a>.<\/p>\r\n\r\n<h3>A Note on the Demo<\/h3>\r\n<p>To illustrate the location of each of the ranges, I needed some way to highlight or mark up a <code class=\"language-html\" >textarea<\/code>. Which is of course impossible. But someone with a screen name of Trinithis created an <a href=\"http:\/\/trinithis.awardspace.com\/regexTester\/regexTester.html\">awesome hack<\/a> (explained in a <a href=\"http:\/\/www.dynamicdrive.com\/forums\/showthread.php?22040-Colorizing-Textarea\">ddforum post<\/a>) that uses absolutely positioned <code class=\"language-html\" >&lt;pre&gt;<\/code> elements behind the textarea, with the textarea itself having a transparent background. I ended up needing to use a timer for the redrawing, which is inelegant, but works in most browsers and avoids issues of synchronicity when listening for events. I packaged this up into a <a href=\"http:\/\/bililite.com\/blog\/2013\/03\/01\/highlighting-textareas\/\" title=\"Highlighting textareas\">jQuery plugin<\/a>, though it's still pretty flaky (as in, doesn't handle text wrapping well).<\/p>","protected":false},"excerpt":{"rendered":"Edited 2014-02-27 to add the indentation code. I created a collection of useful plugins for my bililiteRange, for more sophisticated manipulations. Now you can search the element with a regular expression, and you can keep the range \"live\", adjusting it when the text is edited. Download the code. See the demo. (The dynamic highlighting is [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,10],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2775"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=2775"}],"version-history":[{"count":28,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2775\/revisions"}],"predecessor-version":[{"id":3553,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2775\/revisions\/3553"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=2775"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=2775"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=2775"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}