UNPKG

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