jQuery Simulate Key-Sequence Plugin 1.3.0

jQuery Simulate Key-Sequence Plugin

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/14095/88783/jQuery%20Simulate%20Key-Sequence%20Plugin%20130.js

  1. /*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
  2. /*global jQuery:true $:true bililiteRange:true */
  3.  
  4. /* jQuery Simulate Key-Sequence Plugin 1.3.0
  5. * http://github.com/j-ulrich/jquery-simulate-ext
  6. *
  7. * Copyright (c) 2014 Jochen Ulrich
  8. * Licensed under the MIT license (MIT-LICENSE.txt).
  9. *
  10. * The plugin is an extension and modification of the jQuery sendkeys plugin by Daniel Wachsstock.
  11. * Therefore, the original copyright notice and license follow below.
  12. */
  13.  
  14. // insert characters in a textarea or text input field
  15. // special characters are enclosed in {}; use {{} for the { character itself
  16. // documentation: http://bililite.com/blog/2008/08/20/the-fnsendkeys-plugin/
  17. // Version: 2.0
  18. // Copyright (c) 2010 Daniel Wachsstock
  19. // MIT license:
  20. // Permission is hereby granted, free of charge, to any person
  21. // obtaining a copy of this software and associated documentation
  22. // files (the "Software"), to deal in the Software without
  23. // restriction, including without limitation the rights to use,
  24. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  25. // copies of the Software, and to permit persons to whom the
  26. // Software is furnished to do so, subject to the following
  27. // conditions:
  28. //
  29. // The above copyright notice and this permission notice shall be
  30. // included in all copies or substantial portions of the Software.
  31. //
  32. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  33. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  34. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  35. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  36. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  37. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  38. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  39. // OTHER DEALINGS IN THE SOFTWARE.
  40.  
  41. ;(function($, undefined){
  42. "use strict";
  43. $.simulate.prototype.quirks = $.simulate.prototype.quirks || {};
  44. $.extend($.simulate.prototype.quirks,
  45.  
  46. /**
  47. * @lends $.simulate.prototype.quirks
  48. */
  49. {
  50. /**
  51. * When simulating with delay in non-input elements,
  52. * all spaces are simulated at the end of the sequence instead
  53. * of the correct position.
  54. * @see {@link https://github.com/j-ulrich/jquery-simulate-ext/issues/6|issues #6}
  55. */
  56. delayedSpacesInNonInputGlitchToEnd: undefined
  57.  
  58. });
  59. $.extend($.simulate.prototype,
  60. /**
  61. * @lends $.simulate.prototype
  62. */
  63. {
  64. /**
  65. * Simulates sequencial key strokes.
  66. *
  67. * @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/key-sequence.md
  68. * @public
  69. * @author Daniel Wachsstock, julrich
  70. * @since 1.0
  71. */
  72. simulateKeySequence: function() {
  73. var target = this.target,
  74. $target = $(target),
  75. opts = $.extend({
  76. sequence: "",
  77. triggerKeyEvents: true,
  78. eventProps: {},
  79. delay: 0,
  80. callback: undefined
  81. }, this.options),
  82. sequence = opts.sequence;
  83. opts.delay = parseInt(opts.delay,10);
  84.  
  85. var localkeys = {};
  86.  
  87. // Fix for #6 (https://github.com/j-ulrich/jquery-simulate-ext/issues/6)
  88. if ($.simulate.prototype.quirks.delayedSpacesInNonInputGlitchToEnd && !$target.is('input,textarea')) {
  89. $.extend(localkeys, {
  90. ' ': function(rng, s, opts) {
  91. var internalOpts = $.extend({}, opts, {
  92. triggerKeyEvents: false,
  93. delay: 0,
  94. callback: undefined
  95. });
  96. $.simulate.prototype.simulateKeySequence.defaults.simplechar(rng, '\xA0', internalOpts);
  97. $.simulate.prototype.simulateKeySequence.defaults['{leftarrow}'](rng, s, internalOpts);
  98. $.simulate.prototype.simulateKeySequence.defaults.simplechar(rng, s, opts);
  99. $.simulate.prototype.simulateKeySequence.defaults['{del}'](rng, s, internalOpts);
  100. }
  101. });
  102. }
  103.  
  104. $.extend(localkeys, opts, $target.data('simulate-keySequence')); // allow for element-specific key functions
  105.  
  106. // most elements to not keep track of their selection when they lose focus, so we have to do it for them
  107. var rng = $.data (target, 'simulate-keySequence.selection');
  108. if (!rng){
  109. rng = bililiteRange(target).bounds('selection');
  110. $.data(target, 'simulate-keySequence.selection', rng);
  111. $target.bind('mouseup.simulate-keySequence', function(){
  112. // we have to update the saved range. The routines here update the bounds with each press, but actual keypresses and mouseclicks do not
  113. $.data(target, 'simulate-keySequence.selection').bounds('selection');
  114. }).bind('keyup.simulate-keySequence', function(evt){
  115. // restore the selection if we got here with a tab (a click should select what was clicked on)
  116. if (evt.which === 9){
  117. // there's a flash of selection when we restore the focus, but I don't know how to avoid that.
  118. $.data(target, 'simulate-keySequence.selection').select();
  119. }else{
  120. $.data(target, 'simulate-keySequence.selection').bounds('selection');
  121. }
  122. });
  123. }
  124. $target.focus();
  125. if (typeof sequence === 'undefined') { // no string, so we just set up the event handlers
  126. return;
  127. }
  128. sequence = sequence.replace(/\n/g, '{enter}'); // turn line feeds into explicit break insertions
  129. /**
  130. * Informs the rest of the world that the sequences is finished.
  131. * @fires simulate-keySequence
  132. * @requires target
  133. * @requires sequence
  134. * @requires opts
  135. * @inner
  136. * @author julrich
  137. * @since 1.0
  138. */
  139. function sequenceFinished() {
  140. $target.trigger({type: 'simulate-keySequence', sequence: sequence});
  141. if ($.isFunction(opts.callback)) {
  142. opts.callback.apply(target, [{
  143. sequence: sequence
  144. }]);
  145. }
  146. }
  147. /**
  148. * Simulates the key stroke for one character (or special sequence) and sleeps for
  149. * <code>opts.delay</code> milliseconds.
  150. * @requires lastTime
  151. * @requires now()
  152. * @requires tokenRegExp
  153. * @requires opts
  154. * @requires rng
  155. * @inner
  156. * @author julrich
  157. * @since 1.0
  158. */
  159. function processNextToken() {
  160. var timeElapsed = now() - lastTime; // Work-around for Firefox "bug": setTimeout can fire before the timeout
  161. if (timeElapsed >= opts.delay) {
  162. var match = tokenRegExp.exec(sequence);
  163. if ( match !== null ) {
  164. var s = match[0];
  165. (localkeys[s] || $.simulate.prototype.simulateKeySequence.defaults[s] || $.simulate.prototype.simulateKeySequence.defaults.simplechar)(rng, s, opts);
  166. setTimeout(processNextToken, opts.delay);
  167. }
  168. else {
  169. sequenceFinished();
  170. }
  171. lastTime = now();
  172. }
  173. else {
  174. setTimeout(processNextToken, opts.delay - timeElapsed);
  175. }
  176. }
  177.  
  178. if (!opts.delay || opts.delay <= 0) {
  179. // Run as fast as possible
  180. sequence.replace(/\{[^}]*\}|[^{]+/g, function(s){
  181. (localkeys[s] || $.simulate.prototype.simulateKeySequence.defaults[s] || $.simulate.prototype.simulateKeySequence.defaults.simplechar)(rng, s, opts);
  182. });
  183. sequenceFinished();
  184. }
  185. else {
  186. var tokenRegExp = /\{[^}]*\}|[^{]/g; // This matches curly bracket expressions or single characters
  187. var now = Date.now || function() { return new Date().getTime(); },
  188. lastTime = now();
  189. processNextToken();
  190. }
  191. }
  192. });
  193.  
  194. $.extend($.simulate.prototype.simulateKeySequence.prototype,
  195. /**
  196. * @lends $.simulate.prototype.simulateKeySequence.prototype
  197. */
  198. {
  199. /**
  200. * Maps special character char codes to IE key codes (covers IE and Webkit)
  201. * @author julrich
  202. * @since 1.0
  203. */
  204. IEKeyCodeTable: {
  205. 33: 49, // ! -> 1
  206. 64: 50, // @ -> 2
  207. 35: 51, // # -> 3
  208. 36: 52, // $ -> 4
  209. 37: 53, // % -> 5
  210. 94: 54, // ^ -> 6
  211. 38: 55, // & -> 7
  212. 42: 56, // * -> 8
  213. 40: 57, // ( -> 9
  214. 41: 48, // ) -> 0
  215. 59: 186, // ; -> 186
  216. 58: 186, // : -> 186
  217. 61: 187, // = -> 187
  218. 43: 187, // + -> 187
  219. 44: 188, // , -> 188
  220. 60: 188, // < -> 188
  221. 45: 189, // - -> 189
  222. 95: 189, // _ -> 189
  223. 46: 190, // . -> 190
  224. 62: 190, // > -> 190
  225. 47: 191, // / -> 191
  226. 63: 191, // ? -> 191
  227. 96: 192, // ` -> 192
  228. 126: 192, // ~ -> 192
  229. 91: 219, // [ -> 219
  230. 123: 219, // { -> 219
  231. 92: 220, // \ -> 220
  232. 124: 220, // | -> 220
  233. 93: 221, // ] -> 221
  234. 125: 221, // } -> 221
  235. 39: 222, // ' -> 222
  236. 34: 222 // " -> 222
  237. },
  238. /**
  239. * Tries to convert character codes to key codes.
  240. * @param {Numeric} character - A character code
  241. * @returns {Numeric} The key code corresponding to the given character code,
  242. * based on the key code table of InternetExplorer. If no corresponding key code
  243. * could be found (which will be the case for all special characters except the common
  244. * ones), the character code itself is returned. However, <code>keyCode === charCode</code>
  245. * does not imply that no key code was found because some key codes are identical to the
  246. * character codes (e.g. for uppercase characters).
  247. * @requires $.simulate.prototype.simulateKeySequence.prototype.IEKeyCodeTable
  248. * @see $.simulate.prototype.simulateKeySequence.prototype.IEKeyCodeTable
  249. * @author julrich
  250. * @since 1.0
  251. */
  252. charToKeyCode: function(character) {
  253. var specialKeyCodeTable = $.simulate.prototype.simulateKeySequence.prototype.IEKeyCodeTable;
  254. var charCode = character.charCodeAt(0);
  255. if (charCode >= 64 && charCode <= 90 || charCode >= 48 && charCode <= 57) {
  256. // A-Z and 0-9
  257. return charCode;
  258. }
  259. else if (charCode >= 97 && charCode <= 122) {
  260. // a-z -> A-Z
  261. return character.toUpperCase().charCodeAt(0);
  262. }
  263. else if (specialKeyCodeTable[charCode] !== undefined) {
  264. return specialKeyCodeTable[charCode];
  265. }
  266. else {
  267. return charCode;
  268. }
  269. }
  270. });
  271.  
  272. // add the functions publicly so they can be overridden
  273. $.simulate.prototype.simulateKeySequence.defaults = {
  274. /**
  275. * Simulates key strokes of "normal" characters (i.e. non-special sequences).
  276. * @param {Object} rng - bililiteRange object of the simulation target element.
  277. * @param {String} s - String of (simple) characters to be simulated.
  278. * @param {Object} opts - The key-sequence options.
  279. * @author Daniel Wachsstock, julrich
  280. * @since 1.0
  281. */
  282. simplechar: function (rng, s, opts){
  283. rng.text(s, 'end');
  284. if (opts.triggerKeyEvents) {
  285. for (var i =0; i < s.length; i += 1){
  286. var charCode = s.charCodeAt(i);
  287. var keyCode = $.simulate.prototype.simulateKeySequence.prototype.charToKeyCode(s.charAt(i));
  288. // a bit of cheating: rng._el is the element associated with rng.
  289. $(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: keyCode}));
  290. $(rng._el).simulate('keypress', $.extend({}, opts.eventProps,{keyCode: charCode, which: charCode, charCode: charCode}));
  291. $(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {keyCode: keyCode}));
  292. }
  293. }
  294. },
  295. /**
  296. * Simulates key strokes of a curly opening bracket.
  297. * @param {Object} rng - bililiteRange object of the simulation target element.
  298. * @param {String} s - Ignored.
  299. * @param {Object} opts - The key-sequence options.
  300. * @author Daniel Wachsstock, julrich
  301. * @since 1.0
  302. */
  303. '{{}': function (rng, s, opts){
  304. $.simulate.prototype.simulateKeySequence.defaults.simplechar(rng, '{', opts);
  305. },
  306. /**
  307. * Simulates hitting the enter button.
  308. * @param {Object} rng - bililiteRange object of the simulation target element.
  309. * @param {String} s - Ignored.
  310. * @param {Object} opts - The key-sequence options.
  311. * @author Daniel Wachsstock, julrich
  312. * @since 1.0
  313. */
  314. '{enter}': function (rng, s, opts){
  315. rng.insertEOL();
  316. rng.select();
  317. if (opts.triggerKeyEvents === true) {
  318. $(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: 13}));
  319. $(rng._el).simulate('keypress', $.extend({}, opts.eventProps, {keyCode: 13, which: 13, charCode: 13}));
  320. $(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {keyCode: 13}));
  321. }
  322. },
  323. /**
  324. * Simulates hitting the backspace button.
  325. * @param {Object} rng - bililiteRange object of the simulation target element.
  326. * @param {String} s - Ignored.
  327. * @param {Object} opts - The key-sequence options.
  328. * @author Daniel Wachsstock, julrich
  329. * @since 1.0
  330. */
  331. '{backspace}': function (rng, s, opts){
  332. var b = rng.bounds();
  333. 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
  334. rng.text('', 'end'); // delete the characters and update the selection
  335. if (opts.triggerKeyEvents === true) {
  336. $(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: 8}));
  337. $(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {keyCode: 8}));
  338. }
  339. },
  340. /**
  341. * Simulates hitting the delete button.
  342. * @param {Object} rng - bililiteRange object of the simulation target element.
  343. * @param {String} s - Ignored.
  344. * @param {Object} opts - The key-sequence options.
  345. * @author Daniel Wachsstock, julrich
  346. * @since 1.0
  347. */
  348. '{del}': function (rng, s, opts){
  349. var b = rng.bounds();
  350. if (b[0] === b[1]) { rng.bounds([b[0], b[0]+1]); } // no characters selected; it's just an insertion point. Remove the next character
  351. rng.text('', 'end'); // delete the characters and update the selection
  352. if (opts.triggerKeyEvents === true) {
  353. $(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: 46}));
  354. $(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {keyCode: 46}));
  355. }
  356. },
  357. /**
  358. * Simulates hitting the right arrow button.
  359. * @param {Object} rng - bililiteRange object of the simulation target element.
  360. * @param {String} s - Ignored.
  361. * @param {Object} opts - The key-sequence options.
  362. * @author Daniel Wachsstock, julrich
  363. * @since 1.0
  364. */
  365. '{rightarrow}': function (rng, s, opts){
  366. var b = rng.bounds();
  367. if (b[0] === b[1]) { b[1] += 1; } // no characters selected; it's just an insertion point. Move to the right
  368. rng.bounds([b[1], b[1]]).select();
  369. if (opts.triggerKeyEvents === true) {
  370. $(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: 39}));
  371. $(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {keyCode: 39}));
  372. }
  373. },
  374. /**
  375. * Simulates hitting the left arrow button.
  376. * @param {Object} rng - bililiteRange object of the simulation target element.
  377. * @param {String} s - Ignored.
  378. * @param {Object} opts - The key-sequence options.
  379. * @author Daniel Wachsstock, julrich
  380. * @since 1.0
  381. */
  382. '{leftarrow}': function (rng, s, opts){
  383. var b = rng.bounds();
  384. if (b[0] === b[1]) { b[0] -= 1; } // no characters selected; it's just an insertion point. Move to the left
  385. rng.bounds([b[0], b[0]]).select();
  386. if (opts.triggerKeyEvents === true) {
  387. $(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: 37}));
  388. $(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {keyCode: 37}));
  389. }
  390. },
  391. /**
  392. * Selects all characters in the target element.
  393. * @param {Object} rng - bililiteRange object of the simulation target element.
  394. * @author Daniel Wachsstock, julrich
  395. * @since 1.0
  396. */
  397. '{selectall}' : function (rng){
  398. rng.bounds('all').select();
  399. }
  400. };
  401. //####### Quirk detection #######
  402. if ($.simulate.ext_disableQuirkDetection !== true) { // Fixes issue #9 (https://github.com/j-ulrich/jquery-simulate-ext/issues/9)
  403. $(document).ready(function() {
  404. // delayedSpacesInNonInputGlitchToEnd
  405. // See issues #6 (https://github.com/j-ulrich/jquery-simulate-ext/issues/6)
  406. /* Append a div to the document (bililiteRange needs the element to be in the document), simulate
  407. * a delayed sequence containing a space in the middle and check if the space moves to the end.
  408. */
  409. var $testDiv = $('<div/>').css({height: 1, width: 1, position: 'absolute', left: -1000, top: -1000}).appendTo('body');
  410. $testDiv.simulate('key-sequence', {sequence: '\xA0 \xA0', delay:1, callback: function() {
  411. $.simulate.prototype.quirks.delayedSpacesInNonInputGlitchToEnd = ($testDiv.text() === '\xA0\xA0 ');
  412. $testDiv.remove();
  413. }});
  414. });
  415. }
  416.  
  417. })(jQuery);