UNPKG

22.1 kBJavaScriptView Raw
1/*syn@0.14.0#key*/
2var syn = require('./synthetic.js');
3require('./typeable.js');
4require('./browsers.js');
5var h = syn.helpers, formElExp = /input|textarea/i, supportsSelection = function (el) {
6 var result;
7 try {
8 result = el.selectionStart !== undefined && el.selectionStart !== null;
9 } catch (e) {
10 result = false;
11 }
12 return result;
13 }, getSelection = function (el) {
14 var real, r, start;
15 if (supportsSelection(el)) {
16 if (document.activeElement && document.activeElement !== el && el.selectionStart === el.selectionEnd && el.selectionStart === 0) {
17 return {
18 start: el.value.length,
19 end: el.value.length
20 };
21 }
22 return {
23 start: el.selectionStart,
24 end: el.selectionEnd
25 };
26 } else {
27 try {
28 if (el.nodeName.toLowerCase() === 'input') {
29 real = h.getWindow(el).document.selection.createRange();
30 r = el.createTextRange();
31 r.setEndPoint('EndToStart', real);
32 start = r.text.length;
33 return {
34 start: start,
35 end: start + real.text.length
36 };
37 } else {
38 real = h.getWindow(el).document.selection.createRange();
39 r = real.duplicate();
40 var r2 = real.duplicate(), r3 = real.duplicate();
41 r2.collapse();
42 r3.collapse(false);
43 r2.moveStart('character', -1);
44 r3.moveStart('character', -1);
45 r.moveToElementText(el);
46 r.setEndPoint('EndToEnd', real);
47 start = r.text.length - real.text.length;
48 var end = r.text.length;
49 if (start !== 0 && r2.text === '') {
50 start += 2;
51 }
52 if (end !== 0 && r3.text === '') {
53 end += 2;
54 }
55 return {
56 start: start,
57 end: end
58 };
59 }
60 } catch (e) {
61 var prop = formElExp.test(el.nodeName) ? 'value' : 'textContent';
62 return {
63 start: el[prop].length,
64 end: el[prop].length
65 };
66 }
67 }
68 }, getFocusable = function (el) {
69 var document = h.getWindow(el).document, res = [];
70 var els = document.getElementsByTagName('*'), len = els.length;
71 for (var i = 0; i < len; i++) {
72 if (syn.isFocusable(els[i]) && els[i] !== document.documentElement) {
73 res.push(els[i]);
74 }
75 }
76 return res;
77 }, textProperty = function () {
78 var el = document.createElement('span');
79 return el.textContent != null ? 'textContent' : 'innerText';
80 }(), getText = function (el) {
81 if (formElExp.test(el.nodeName)) {
82 return el.value;
83 }
84 return el[textProperty];
85 }, setText = function (el, value) {
86 if (formElExp.test(el.nodeName)) {
87 el.value = value;
88 } else {
89 el[textProperty] = value;
90 }
91 };
92h.extend(syn, {
93 keycodes: {
94 '\b': 8,
95 '\t': 9,
96 '\r': 13,
97 'shift': 16,
98 'ctrl': 17,
99 'alt': 18,
100 'meta': 91,
101 'pause-break': 19,
102 'caps': 20,
103 'escape': 27,
104 'num-lock': 144,
105 'scroll-lock': 145,
106 'print': 44,
107 'page-up': 33,
108 'page-down': 34,
109 'end': 35,
110 'home': 36,
111 'left': 37,
112 'up': 38,
113 'right': 39,
114 'down': 40,
115 'insert': 45,
116 'delete': 46,
117 ' ': 32,
118 '0': 48,
119 '1': 49,
120 '2': 50,
121 '3': 51,
122 '4': 52,
123 '5': 53,
124 '6': 54,
125 '7': 55,
126 '8': 56,
127 '9': 57,
128 'a': 65,
129 'b': 66,
130 'c': 67,
131 'd': 68,
132 'e': 69,
133 'f': 70,
134 'g': 71,
135 'h': 72,
136 'i': 73,
137 'j': 74,
138 'k': 75,
139 'l': 76,
140 'm': 77,
141 'n': 78,
142 'o': 79,
143 'p': 80,
144 'q': 81,
145 'r': 82,
146 's': 83,
147 't': 84,
148 'u': 85,
149 'v': 86,
150 'w': 87,
151 'x': 88,
152 'y': 89,
153 'z': 90,
154 'num0': 96,
155 'num1': 97,
156 'num2': 98,
157 'num3': 99,
158 'num4': 100,
159 'num5': 101,
160 'num6': 102,
161 'num7': 103,
162 'num8': 104,
163 'num9': 105,
164 '*': 106,
165 '+': 107,
166 'subtract': 109,
167 'decimal': 110,
168 'divide': 111,
169 ';': 186,
170 '=': 187,
171 ',': 188,
172 'dash': 189,
173 '-': 189,
174 'period': 190,
175 '.': 190,
176 'forward-slash': 191,
177 '/': 191,
178 '`': 192,
179 '[': 219,
180 '\\': 220,
181 ']': 221,
182 '\'': 222,
183 'left window key': 91,
184 'right window key': 92,
185 'select key': 93,
186 'f1': 112,
187 'f2': 113,
188 'f3': 114,
189 'f4': 115,
190 'f5': 116,
191 'f6': 117,
192 'f7': 118,
193 'f8': 119,
194 'f9': 120,
195 'f10': 121,
196 'f11': 122,
197 'f12': 123
198 },
199 selectText: function (el, start, end) {
200 if (supportsSelection(el)) {
201 if (!end) {
202 syn.__tryFocus(el);
203 el.setSelectionRange(start, start);
204 } else {
205 el.selectionStart = start;
206 el.selectionEnd = end;
207 }
208 } else if (el.createTextRange) {
209 var r = el.createTextRange();
210 r.moveStart('character', start);
211 end = end || start;
212 r.moveEnd('character', end - el.value.length);
213 r.select();
214 }
215 },
216 getText: function (el) {
217 if (syn.typeable.test(el)) {
218 var sel = getSelection(el);
219 return el.value.substring(sel.start, sel.end);
220 }
221 var win = syn.helpers.getWindow(el);
222 if (win.getSelection) {
223 return win.getSelection().toString();
224 } else if (win.document.getSelection) {
225 return win.document.getSelection().toString();
226 } else {
227 return win.document.selection.createRange().text;
228 }
229 },
230 getSelection: getSelection
231});
232h.extend(syn.key, {
233 data: function (key) {
234 if (syn.key.browser[key]) {
235 return syn.key.browser[key];
236 }
237 for (var kind in syn.key.kinds) {
238 if (h.inArray(key, syn.key.kinds[kind]) > -1) {
239 return syn.key.browser[kind];
240 }
241 }
242 return syn.key.browser.character;
243 },
244 isSpecial: function (keyCode) {
245 var specials = syn.key.kinds.special;
246 for (var i = 0; i < specials.length; i++) {
247 if (syn.keycodes[specials[i]] === keyCode) {
248 return specials[i];
249 }
250 }
251 },
252 options: function (key, event) {
253 var keyData = syn.key.data(key);
254 if (!keyData[event]) {
255 return null;
256 }
257 var charCode = keyData[event][0], keyCode = keyData[event][1], result = {};
258 if (keyCode === 'key') {
259 result.keyCode = syn.keycodes[key];
260 } else if (keyCode === 'char') {
261 result.keyCode = key.charCodeAt(0);
262 } else {
263 result.keyCode = keyCode;
264 }
265 if (charCode === 'char') {
266 result.charCode = key.charCodeAt(0);
267 } else if (charCode !== null) {
268 result.charCode = charCode;
269 }
270 if (result.keyCode) {
271 result.which = result.keyCode;
272 } else {
273 result.which = result.charCode;
274 }
275 return result;
276 },
277 kinds: {
278 special: [
279 'shift',
280 'ctrl',
281 'alt',
282 'meta',
283 'caps'
284 ],
285 specialChars: ['\b'],
286 navigation: [
287 'page-up',
288 'page-down',
289 'end',
290 'home',
291 'left',
292 'up',
293 'right',
294 'down',
295 'insert',
296 'delete'
297 ],
298 'function': [
299 'f1',
300 'f2',
301 'f3',
302 'f4',
303 'f5',
304 'f6',
305 'f7',
306 'f8',
307 'f9',
308 'f10',
309 'f11',
310 'f12'
311 ]
312 },
313 getDefault: function (key) {
314 if (syn.key.defaults[key]) {
315 return syn.key.defaults[key];
316 }
317 for (var kind in syn.key.kinds) {
318 if (h.inArray(key, syn.key.kinds[kind]) > -1 && syn.key.defaults[kind]) {
319 return syn.key.defaults[kind];
320 }
321 }
322 return syn.key.defaults.character;
323 },
324 defaults: {
325 'character': function (options, scope, key, force, sel) {
326 if (/num\d+/.test(key)) {
327 key = key.match(/\d+/)[0];
328 }
329 if (force || !syn.support.keyCharacters && syn.typeable.test(this)) {
330 var current = getText(this), before = current.substr(0, sel.start), after = current.substr(sel.end), character = key;
331 setText(this, before + character + after);
332 var charLength = character === '\n' && syn.support.textareaCarriage ? 2 : character.length;
333 syn.selectText(this, before.length + charLength);
334 }
335 },
336 'c': function (options, scope, key, force, sel) {
337 if (syn.key.ctrlKey) {
338 syn.key.clipboard = syn.getText(this);
339 } else {
340 syn.key.defaults.character.apply(this, arguments);
341 }
342 },
343 'v': function (options, scope, key, force, sel) {
344 if (syn.key.ctrlKey) {
345 syn.key.defaults.character.call(this, options, scope, syn.key.clipboard, true, sel);
346 } else {
347 syn.key.defaults.character.apply(this, arguments);
348 }
349 },
350 'a': function (options, scope, key, force, sel) {
351 if (syn.key.ctrlKey) {
352 syn.selectText(this, 0, getText(this).length);
353 } else {
354 syn.key.defaults.character.apply(this, arguments);
355 }
356 },
357 'home': function () {
358 syn.onParents(this, function (el) {
359 if (el.scrollHeight !== el.clientHeight) {
360 el.scrollTop = 0;
361 return false;
362 }
363 });
364 },
365 'end': function () {
366 syn.onParents(this, function (el) {
367 if (el.scrollHeight !== el.clientHeight) {
368 el.scrollTop = el.scrollHeight;
369 return false;
370 }
371 });
372 },
373 'page-down': function () {
374 syn.onParents(this, function (el) {
375 if (el.scrollHeight !== el.clientHeight) {
376 var ch = el.clientHeight;
377 el.scrollTop += ch;
378 return false;
379 }
380 });
381 },
382 'page-up': function () {
383 syn.onParents(this, function (el) {
384 if (el.scrollHeight !== el.clientHeight) {
385 var ch = el.clientHeight;
386 el.scrollTop -= ch;
387 return false;
388 }
389 });
390 },
391 '\b': function (options, scope, key, force, sel) {
392 if (!syn.support.backspaceWorks && syn.typeable.test(this)) {
393 var current = getText(this), before = current.substr(0, sel.start), after = current.substr(sel.end);
394 if (sel.start === sel.end && sel.start > 0) {
395 setText(this, before.substring(0, before.length - 1) + after);
396 syn.selectText(this, sel.start - 1);
397 } else {
398 setText(this, before + after);
399 syn.selectText(this, sel.start);
400 }
401 }
402 },
403 'delete': function (options, scope, key, force, sel) {
404 if (!syn.support.backspaceWorks && syn.typeable.test(this)) {
405 var current = getText(this), before = current.substr(0, sel.start), after = current.substr(sel.end);
406 if (sel.start === sel.end && sel.start <= getText(this).length - 1) {
407 setText(this, before + after.substring(1));
408 } else {
409 setText(this, before + after);
410 }
411 syn.selectText(this, sel.start);
412 }
413 },
414 '\r': function (options, scope, key, force, sel) {
415 var nodeName = this.nodeName.toLowerCase();
416 if (nodeName === 'input') {
417 syn.trigger(this, 'change', {});
418 }
419 if (!syn.support.keypressSubmits && nodeName === 'input') {
420 var form = syn.closest(this, 'form');
421 if (form) {
422 syn.trigger(form, 'submit', {});
423 }
424 }
425 if (!syn.support.keyCharacters && nodeName === 'textarea') {
426 syn.key.defaults.character.call(this, options, scope, '\n', undefined, sel);
427 }
428 if (!syn.support.keypressOnAnchorClicks && nodeName === 'a') {
429 syn.trigger(this, 'click', {});
430 }
431 },
432 '\t': function (options, scope) {
433 var focusEls = getFocusable(this), current = null, i = 0, el, firstNotIndexed, orders = [];
434 for (; i < focusEls.length; i++) {
435 orders.push([
436 focusEls[i],
437 i
438 ]);
439 }
440 var sort = function (order1, order2) {
441 var el1 = order1[0], el2 = order2[0], tab1 = syn.tabIndex(el1) || 0, tab2 = syn.tabIndex(el2) || 0;
442 if (tab1 === tab2) {
443 return order1[1] - order2[1];
444 } else {
445 if (tab1 === 0) {
446 return 1;
447 } else if (tab2 === 0) {
448 return -1;
449 } else {
450 return tab1 - tab2;
451 }
452 }
453 };
454 orders.sort(sort);
455 var ordersLength = orders.length;
456 for (i = 0; i < ordersLength; i++) {
457 el = orders[i][0];
458 if (this === el) {
459 var nextIndex = i;
460 if (syn.key.shiftKey) {
461 nextIndex--;
462 current = nextIndex >= 0 && orders[nextIndex][0] || orders[ordersLength - 1][0];
463 } else {
464 nextIndex++;
465 current = nextIndex < ordersLength && orders[nextIndex][0] || orders[0][0];
466 }
467 }
468 }
469 if (!current) {
470 current = firstNotIndexed;
471 } else {
472 syn.__tryFocus(current);
473 }
474 return current;
475 },
476 'left': function (options, scope, key, force, sel) {
477 if (syn.typeable.test(this)) {
478 if (syn.key.shiftKey) {
479 syn.selectText(this, sel.start === 0 ? 0 : sel.start - 1, sel.end);
480 } else {
481 syn.selectText(this, sel.start === 0 ? 0 : sel.start - 1);
482 }
483 }
484 },
485 'right': function (options, scope, key, force, sel) {
486 if (syn.typeable.test(this)) {
487 if (syn.key.shiftKey) {
488 syn.selectText(this, sel.start, sel.end + 1 > getText(this).length ? getText(this).length : sel.end + 1);
489 } else {
490 syn.selectText(this, sel.end + 1 > getText(this).length ? getText(this).length : sel.end + 1);
491 }
492 }
493 },
494 'up': function () {
495 if (/select/i.test(this.nodeName)) {
496 this.selectedIndex = this.selectedIndex ? this.selectedIndex - 1 : 0;
497 }
498 },
499 'down': function () {
500 if (/select/i.test(this.nodeName)) {
501 syn.changeOnBlur(this, 'selectedIndex', this.selectedIndex);
502 this.selectedIndex = this.selectedIndex + 1;
503 }
504 },
505 'shift': function () {
506 return null;
507 },
508 'ctrl': function () {
509 return null;
510 },
511 'alt': function () {
512 return null;
513 },
514 'meta': function () {
515 return null;
516 }
517 }
518});
519h.extend(syn.create, {
520 keydown: {
521 setup: function (type, options, element) {
522 if (h.inArray(options, syn.key.kinds.special) !== -1) {
523 syn.key[options + 'Key'] = element;
524 }
525 }
526 },
527 keypress: {
528 setup: function (type, options, element) {
529 if (syn.support.keyCharacters && !syn.support.keysOnNotFocused) {
530 syn.__tryFocus(element);
531 }
532 }
533 },
534 keyup: {
535 setup: function (type, options, element) {
536 if (h.inArray(options, syn.key.kinds.special) !== -1) {
537 syn.key[options + 'Key'] = null;
538 }
539 }
540 },
541 key: {
542 options: function (type, options, element) {
543 options = typeof options !== 'object' ? { character: options } : options;
544 options = h.extend({}, options);
545 if (options.character) {
546 h.extend(options, syn.key.options(options.character, type));
547 delete options.character;
548 }
549 options = h.extend({
550 ctrlKey: !!syn.key.ctrlKey,
551 altKey: !!syn.key.altKey,
552 shiftKey: !!syn.key.shiftKey,
553 metaKey: !!syn.key.metaKey
554 }, options);
555 return options;
556 },
557 event: function (type, options, element) {
558 var doc = h.getWindow(element).document || document, event;
559 if (doc.createEvent) {
560 try {
561 event = doc.createEvent('KeyEvents');
562 event.initKeyEvent(type, true, true, window, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode, options.charCode);
563 } catch (e) {
564 event = h.createBasicStandardEvent(type, options, doc);
565 }
566 event.synthetic = true;
567 return event;
568 } else {
569 try {
570 event = h.createEventObject.apply(this, arguments);
571 h.extend(event, options);
572 } catch (e) {
573 }
574 return event;
575 }
576 }
577 }
578});
579var convert = {
580 'enter': '\r',
581 'backspace': '\b',
582 'tab': '\t',
583 'space': ' '
584};
585h.extend(syn.init.prototype, {
586 _key: function (element, options, callback) {
587 if (/-up$/.test(options) && h.inArray(options.replace('-up', ''), syn.key.kinds.special) !== -1) {
588 syn.trigger(element, 'keyup', options.replace('-up', ''));
589 return callback(true, element);
590 }
591 var activeElement = h.getWindow(element).document.activeElement, caret = syn.typeable.test(element) && getSelection(element), key = convert[options] || options, runDefaults = syn.trigger(element, 'keydown', key), getDefault = syn.key.getDefault, prevent = syn.key.browser.prevent, defaultResult, keypressOptions = syn.key.options(key, 'keypress');
592 if (runDefaults) {
593 if (!keypressOptions) {
594 defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret);
595 } else {
596 if (activeElement !== h.getWindow(element).document.activeElement) {
597 element = h.getWindow(element).document.activeElement;
598 }
599 runDefaults = syn.trigger(element, 'keypress', keypressOptions);
600 if (runDefaults) {
601 defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret);
602 }
603 }
604 } else {
605 if (keypressOptions && h.inArray('keypress', prevent.keydown) === -1) {
606 if (activeElement !== h.getWindow(element).document.activeElement) {
607 element = h.getWindow(element).document.activeElement;
608 }
609 syn.trigger(element, 'keypress', keypressOptions);
610 }
611 }
612 if (defaultResult && defaultResult.nodeName) {
613 element = defaultResult;
614 }
615 if (defaultResult !== null) {
616 syn.schedule(function () {
617 if (key === '\r' && element.nodeName.toLowerCase() === 'input') {
618 } else if (syn.support.oninput) {
619 syn.trigger(element, 'input', syn.key.options(key, 'input'));
620 }
621 syn.trigger(element, 'keyup', syn.key.options(key, 'keyup'));
622 callback(runDefaults, element);
623 }, 1);
624 } else {
625 callback(runDefaults, element);
626 }
627 return element;
628 },
629 _type: function (element, options, callback) {
630 var parts = (options + '').match(/(\[[^\]]+\])|([^\[])/g), self = this, runNextPart = function (runDefaults, el) {
631 var part = parts.shift();
632 if (!part) {
633 callback(runDefaults, el);
634 return;
635 }
636 el = el || element;
637 if (part.length > 1) {
638 part = part.substr(1, part.length - 2);
639 }
640 self._key(el, part, runNextPart);
641 };
642 runNextPart();
643 }
644});
\No newline at end of file