{"id":3432,"date":"2015-01-14T14:03:38","date_gmt":"2015-01-14T20:03:38","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=3432"},"modified":"2015-01-14T14:03:38","modified_gmt":"2015-01-14T20:03:38","slug":"bililiterange-sendkeys","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2015\/01\/14\/bililiterange-sendkeys\/","title":{"rendered":"<code>bililiteRange.sendkeys<\/code>"},"content":{"rendered":"<p><a href=\"\/blog\/2011\/01\/17\/cross-browser-text-ranges-and-selections\/\"><code class=\"language-javascript\" >bililiteRange.text()<\/code><\/a> works well to insert text into ranges, but I wanted to be able to simulate other keys, ala <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/8c6yea83%28VS.85%29.aspx\">Microsoft's <code>SendKeys<\/code><\/a>. <code class=\"language-javascript\" >bililiteRange.sendkeys()<\/code> does exactly that. It basically executes <code class=\"language-javascript\" >text(string, 'end')<\/code> but interprets any text between braces (<code class=\"language-javascript\" >'{key}'<\/code>) as a command representing a special key. For security reasons, the browser won't let you do anything outside of the text of the page itself, but I've implemented the following (the key names are from the proposed <a href=\"http:\/\/www.w3.org\/TR\/DOM-Level-3-Events-key\/\">DOM3 standard<\/a>)::<\/p>\n<dl>\n<dt><code>Backspace<\/code><\/dt>\n<dd>Delete backwards<\/dd>\n<dt><code>Delete<\/code><\/dt>\n<dd>Delete forwards<\/dd>\n<dt><code>ArrowRight<\/code><\/dt>\n<dd>Move the insertion point to the right<\/dd>\n<dt><code>ArrowLeft<\/code><\/dt>\n<dd>Move the insertion point to the left<\/dd>\n<dt><code>Enter<\/code><\/dt>\n<dd>Insert a newline, with <code class=\"language-javascript\" >bililiteRange.insertEOL()<\/code>. <strong>Warning:<\/strong> In <code>contenteditable<\/code> elements, <code>Enter<\/code> is flaky and inconsistent across browsers. This is due to the flakiness of <code>contenteditable<\/code> itself; I can't figure out what to do about this.<\/dd>\n<\/dl>\n<p>For backwards-compatibility with older versions, the following synonyms also work: <code>backspace<\/code>, <code>del<\/code>, <code>rightarrow<\/code>, <code>leftarrow<\/code> and <code>enter<\/code>.<\/p>\n<p>So, for example, <code class=\"language-javascript\" >bililiteRange(el).sendkeys('foo')<\/code> replaces the current range with <code class=\"language-javascript\" >'foo'<\/code> and sets the range to just after that string. <code class=\"language-javascript\" >bililiteRange(el).sendkeys('foo{Delete}{ArrowLeft}{ArrowLeft}')<\/code> replaces the current range with <code class=\"language-javascript\" >'foo'<\/code>, removes the character just after that string and sets the range to between the 'f' and the 'o'.<\/p>\n<p>To manipulate the selection, use the usual bililiteRange methods. Thus, to simulate a backspace key, use <code class=\"language-javascript\" >bililiteRange(el).bounds('selection').sendkeys('{Backspace}').select()<\/code>.<br \/>\nTo insert a '{', use an unmatched brace, <code class=\"language-javascript\" >bililiteRange(el).sendkeys('this is a left brace: {')<\/code>,  or <code>{{}<\/code>, as in <code class=\"language-javascript\" >bililiteRange(el).sendkeys('function() {{} whatever }');<\/code>.<\/p>\n<p>If anyone knows how to implement an up or down arrow, or page up\/down, please let me know.<\/p>\n<h3>Other Commands<\/h3>\n<p>To make life easier for me, there are a few other \"keys\" that implement specific actions:<\/p>\n<dl>\n<dt><code>selectall<\/code><\/dt>\n<dd>Select the entire field<\/dd>\n<dt><code>tab<\/code><\/dt>\n<dd>Insert a <code class=\"language-javascript\" >'\\t'<\/code> character. <code class=\"language-javascript\" >$().sendkeys('\\t')<\/code> would work just as well, but there are circumstances when I wanted to avoid having to escape backslashes.<\/dd>\n<dt><code>newline<\/code><\/dt>\n<dd>Insert a <code class=\"language-javascript\" >'\\n'<\/code> character, without the mangling that <code>{enter}<\/code> does.<\/dd>\n<dt><code>selection<\/code><\/dt>\n<dd>Inserts the text of the original selection (useful for creating \"wrapping\" functions, like <code>\"&lt;em&gt;{selection}&lt;\/em&gt;\"<\/code>).<\/dd>\n<dt><code>mark<\/code><\/dt>\n<dd>Remembers the current insertion point and restores it after the <code>sendkeys<\/code> call. Thus <code>\"&lt;p&gt;{mark}&lt;\/p&gt;\"<\/code> inserts <code>&lt;p&gt;&lt;\/p&gt;<\/code> and leaves the insertion point between the tags.<\/dd>\n<\/dl>\n<p>So to wrap the text of a range in HTML tags, use <code class=\"language-javascript\" >range.sendkeys('&lt;strong&gt;{selection}&lt;\/strong&gt;')<\/code>. To create a hyperlink, use <code class=\"language-javascript\" >range.sendkeys('&lt;a href=\"{mark}\"&gt;{selection}&lt;\/a&gt;')<\/code> which leaves the range between the quote marks rather than at the end.<\/p>\n<h3>Plugins<\/h3>\n<p>Adding new commands is easy. All the commands are in the <code class=\"language-javascript\" >bililiteRange.sendkeys<\/code> object, indexed by the name of the command <em>in braces<\/em> (since that made parsing easier). The commands are of the form <code class=\"language-javascript\" >function (rng, c, simplechar)<\/code> where <code class=\"language-javascript\" >rng<\/code> is the target bililiteRange, <code class=\"language-javascript\" >c<\/code> is the command name (in braces), and <code class=\"language-javascript\" >simplechar<\/code> is a <code class=\"language-javascript\" >function simplechar (range, string)<\/code> that will insert <code class=\"language-javascript\" >string<\/code> into the range. <code class=\"language-javascript\" >range.data().sendkeysOriginalText<\/code> is set to the original text of the range, and <code class=\"language-javascript\" >rng.data().sendkeysBounds<\/code> is the argument for <code class=\"language-javascript\" >bililiteRange.bounds()<\/code> that will be used at the end.<\/p>\n<p>So, for example: <\/p>\n<pre><code class=\"language-javascript\" >bililiteRange.sendkeys['{tab}'] = function (range, c, simplechar) { simplechar(rng, '\\t') };\r\nbililiteRange['{Backspace}'] = function (range, c, simplechar){\r\n  var b = rng.bounds();\r\n  if (b[0] == b[1]) rng.bounds([b[0]-1, b[0]]); \/\/ no characters selected; it's just an insertion point. Remove the previous character\r\n  rng.text('', 'end'); \/\/ delete the characters and update the selection\r\n};\r\nbililiteRange.sendkeys['{selectall}'] = function (range, c, simplechar) { rng.bounds('all') };\r\n<\/code><\/pre>\n<p>So to have a reverse-string command:<\/p>\n<pre><code class=\"language-javascript\" >bililiteRange['{reverse}'] = function (range, c, simplechar){\r\n  simplechar(range, range.sendkeysOriginalText.split('').reverse().join(''));\r\n};<\/code><\/pre>\n<p>Or, to annoy the <a href=\"http:\/\/premium.wpmudev.org\/blog\/hello-dolly-really\/\">anti-WordPress crowd<\/a>, a <a href=\"https:\/\/wordpress.org\/plugins\/hello-dolly\/\">Hello, Dolly<\/a> command:<\/p>\n<pre><code class=\"language-javascript\" >bililiteRange['{dolly}'] = function (range, c, simplechar){\r\n  var lyrics = [\r\n    \"Hello, Dolly\",\r\n    \"Well, hello, Dolly\",\r\n    \"It's so nice to have you back where you belong\",\r\n    \"You're lookin' swell, Dolly\",\r\n    \"I can tell, Dolly\",\r\n    \"You're still glowin', you're still crowin'\",\r\n    \"You're still goin' strong\"];\r\n  simplechar (range, lyrics[Math.floor(Math.random() * lyrics.length)];\r\n};<\/code><\/pre>\n<h3>Events<\/h3>\n<p>After each printing character (not the specials, unless they call <code class=\"language-javascript\" >simplechar<\/code>) it triggers a <code>keydown<\/code> event with <code>event.which<\/code>, <code>event.keyCode<\/code> and <code>event.charCode<\/code> set to the Unicode value of the character.<br \/>\nAfter the entire string is processed, it triggers a custom <code>sendkeys<\/code> event with <code>event.which<\/code> set to the original string. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>bililiteRange.text() works well to insert text into ranges, but I wanted to be able to simulate other keys, ala Microsoft's SendKeys. bililiteRange.sendkeys() does exactly that. It basically executes text(string, 'end') but interprets any text between braces ('{key}') as a command representing a special key. For security reasons, the browser won't let you do anything outside [&hellip;]<\/p>\n","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\/3432"}],"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=3432"}],"version-history":[{"count":12,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3432\/revisions"}],"predecessor-version":[{"id":3444,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3432\/revisions\/3444"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=3432"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=3432"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=3432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}