Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: third_party/google_input_tools/src/chrome/os/inputview/controller.js

Issue 674153004: Add third_party/google-input-tools: Take 2 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@google_input_tools
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The ChromeOS IME Authors. All Rights Reserved.
2 // limitations under the License.
3 // See the License for the specific language governing permissions and
4 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5 // distributed under the License is distributed on an "AS-IS" BASIS,
6 // Unless required by applicable law or agreed to in writing, software
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // You may obtain a copy of the License at
11 // you may not use this file except in compliance with the License.
12 // Licensed under the Apache License, Version 2.0 (the "License");
13 //
14 goog.provide('i18n.input.chrome.inputview.Controller');
15
16 goog.require('goog.Disposable');
17 goog.require('goog.array');
18 goog.require('goog.async.Delay');
19 goog.require('goog.dom');
20 goog.require('goog.dom.classlist');
21 goog.require('goog.events.EventHandler');
22 goog.require('goog.events.EventType');
23 goog.require('goog.i18n.bidi');
24 goog.require('goog.object');
25 goog.require('i18n.input.chrome.DataSource');
26 goog.require('i18n.input.chrome.inputview.Adapter');
27 goog.require('i18n.input.chrome.inputview.CandidatesInfo');
28 goog.require('i18n.input.chrome.inputview.ConditionName');
29 goog.require('i18n.input.chrome.inputview.Css');
30 goog.require('i18n.input.chrome.inputview.KeyboardContainer');
31 goog.require('i18n.input.chrome.inputview.M17nModel');
32 goog.require('i18n.input.chrome.inputview.Model');
33 goog.require('i18n.input.chrome.inputview.PerfTracker');
34 goog.require('i18n.input.chrome.inputview.ReadyState');
35 goog.require('i18n.input.chrome.inputview.Settings');
36 goog.require('i18n.input.chrome.inputview.SizeSpec');
37 goog.require('i18n.input.chrome.inputview.SoundController');
38 goog.require('i18n.input.chrome.inputview.SpecNodeName');
39 goog.require('i18n.input.chrome.inputview.StateType');
40 goog.require('i18n.input.chrome.inputview.Statistics');
41 goog.require('i18n.input.chrome.inputview.SwipeDirection');
42 goog.require('i18n.input.chrome.inputview.elements.ElementType');
43 goog.require('i18n.input.chrome.inputview.elements.content.Candidate');
44 goog.require('i18n.input.chrome.inputview.elements.content.CandidateView');
45 goog.require('i18n.input.chrome.inputview.elements.content.ExpandedCandidateView ');
46 goog.require('i18n.input.chrome.inputview.elements.content.MenuView');
47 goog.require('i18n.input.chrome.inputview.events.EventType');
48 goog.require('i18n.input.chrome.inputview.events.KeyCodes');
49 goog.require('i18n.input.chrome.inputview.handler.PointerHandler');
50 goog.require('i18n.input.chrome.inputview.util');
51 goog.require('i18n.input.chrome.message.ContextType');
52 goog.require('i18n.input.chrome.message.Name');
53 goog.require('i18n.input.chrome.message.Type');
54 goog.require('i18n.input.lang.InputToolCode');
55
56
57
58 goog.scope(function() {
59 var CandidateType = i18n.input.chrome.inputview.elements.content.Candidate.Type;
60 var Candidate = i18n.input.chrome.inputview.elements.content.Candidate;
61 var CandidateView = i18n.input.chrome.inputview.elements.content.CandidateView;
62 var ConditionName = i18n.input.chrome.inputview.ConditionName;
63 var ContextType = i18n.input.chrome.message.ContextType;
64 var Css = i18n.input.chrome.inputview.Css;
65 var ElementType = i18n.input.chrome.inputview.elements.ElementType;
66 var EventType = i18n.input.chrome.inputview.events.EventType;
67 var ExpandedCandidateView = i18n.input.chrome.inputview.elements.content.
68 ExpandedCandidateView;
69 var InputToolCode = i18n.input.lang.InputToolCode;
70 var KeyCodes = i18n.input.chrome.inputview.events.KeyCodes;
71 var MenuView = i18n.input.chrome.inputview.elements.content.MenuView;
72 var Name = i18n.input.chrome.message.Name;
73 var PerfTracker = i18n.input.chrome.inputview.PerfTracker;
74 var SizeSpec = i18n.input.chrome.inputview.SizeSpec;
75 var SpecNodeName = i18n.input.chrome.inputview.SpecNodeName;
76 var StateType = i18n.input.chrome.inputview.StateType;
77 var content = i18n.input.chrome.inputview.elements.content;
78 var SoundController = i18n.input.chrome.inputview.SoundController;
79 var Sounds = i18n.input.chrome.inputview.Sounds;
80 var util = i18n.input.chrome.inputview.util;
81
82
83
84 /**
85 * The controller of the input view keyboard.
86 *
87 * @param {string} keyset The keyboard keyset.
88 * @param {string} languageCode The language code for this keyboard.
89 * @param {string} passwordLayout The layout for password box.
90 * @param {string} name The input tool name.
91 * @constructor
92 * @extends {goog.Disposable}
93 */
94 i18n.input.chrome.inputview.Controller = function(keyset, languageCode,
95 passwordLayout, name) {
96 /**
97 * The model.
98 *
99 * @type {!i18n.input.chrome.inputview.Model}
100 * @private
101 */
102 this.model_ = new i18n.input.chrome.inputview.Model();
103
104 /** @private {!i18n.input.chrome.inputview.PerfTracker} */
105 this.perfTracker_ = new i18n.input.chrome.inputview.PerfTracker();
106
107 /**
108 * The layout map.
109 *
110 * @type {!Object.<string, !Object>}
111 * @private
112 */
113 this.layoutDataMap_ = {};
114
115 /**
116 * The keyset data map.
117 *
118 * @type {!Object.<string, !Object>}
119 * @private
120 */
121 this.keysetDataMap_ = {};
122
123 /**
124 * The event handler.
125 *
126 * @type {!goog.events.EventHandler}
127 * @private
128 */
129 this.handler_ = new goog.events.EventHandler(this);
130
131 /**
132 * The m17n model.
133 *
134 * @type {!i18n.input.chrome.inputview.M17nModel}
135 * @private
136 */
137 this.m17nModel_ = new i18n.input.chrome.inputview.M17nModel();
138
139 /**
140 * The pointer handler.
141 *
142 * @type {!i18n.input.chrome.inputview.handler.PointerHandler}
143 * @private
144 */
145 this.pointerHandler_ = new i18n.input.chrome.inputview.handler.
146 PointerHandler();
147
148 /**
149 * The statistics object for recording metrics values.
150 *
151 * @type {!i18n.input.chrome.inputview.Statistics}
152 * @private
153 */
154 this.statistics_ = i18n.input.chrome.inputview.Statistics.getInstance();
155
156 /** @private {!i18n.input.chrome.inputview.ReadyState} */
157 this.readyState_ = new i18n.input.chrome.inputview.ReadyState();
158
159 /** @private {!i18n.input.chrome.inputview.Adapter} */
160 this.adapter_ = new i18n.input.chrome.inputview.Adapter(this.readyState_);
161
162 /** @private {!i18n.input.chrome.inputview.KeyboardContainer} */
163 this.container_ = new i18n.input.chrome.inputview.KeyboardContainer(
164 this.adapter_);
165 this.container_.render();
166
167 /** @private {!i18n.input.chrome.inputview.SoundController} */
168 this.soundController_ = new SoundController(false);
169
170 /**
171 * The context type and keyset mapping group by input method id.
172 * key: input method id.
173 * value: Object
174 * key: context type string.
175 * value: keyset string.
176 *
177 * @private {!Object.<string, !Object.<string, string>>}
178 */
179 this.contextTypeToKeysetMap_ = {};
180
181 this.initialize(keyset, languageCode, passwordLayout, name);
182 /**
183 * The suggestions.
184 * Note: sets a default empty result to avoid null check.
185 *
186 * @private {!i18n.input.chrome.inputview.CandidatesInfo}
187 */
188 this.candidatesInfo_ = i18n.input.chrome.inputview.CandidatesInfo.getEmpty();
189
190 this.registerEventHandler_();
191 };
192 goog.inherits(i18n.input.chrome.inputview.Controller,
193 goog.Disposable);
194 var Controller = i18n.input.chrome.inputview.Controller;
195
196
197 /**
198 * @define {boolean} Flag to disable handwriting.
199 */
200 Controller.DISABLE_HWT = false;
201
202
203 /**
204 * A flag to indicate whether the shift is for auto capital.
205 *
206 * @private {boolean}
207 */
208 Controller.prototype.shiftForAutoCapital_ = false;
209
210
211 /**
212 * @define {boolean} Flag to indicate whether it is debugging.
213 */
214 Controller.DEV = false;
215
216
217 /**
218 * The handwriting view code, use the code can switch handwriting panel view.
219 *
220 * @const {string}
221 * @private
222 */
223 Controller.HANDWRITING_VIEW_CODE_ = 'hwt';
224
225
226 /**
227 * The emoji view code, use the code can switch to emoji.
228 *
229 * @const {string}
230 * @private
231 */
232 Controller.EMOJI_VIEW_CODE_ = 'emoji';
233
234
235 /**
236 * The limitation for backspace repeat time to avoid unexpected
237 * problem that backspace is held all the time.
238 *
239 * @private {number}
240 */
241 Controller.BACKSPACE_REPEAT_LIMIT_ = 255;
242
243
244 /**
245 * The repeated times of the backspace.
246 *
247 * @private {number}
248 */
249 Controller.prototype.backspaceRepeated_ = 0;
250
251
252 /**
253 * The handwriting input tool code suffix.
254 *
255 * @const {string}
256 * @private
257 */
258 Controller.HANDWRITING_CODE_SUFFIX_ = '-t-i0-handwrit';
259
260
261 /**
262 * True if the settings is loaded.
263 *
264 * @type {boolean}
265 */
266 Controller.prototype.isSettingReady = false;
267
268
269 /**
270 * True if the keyboard is set up.
271 * Note: This flag is only used for automation testing.
272 *
273 * @type {boolean}
274 */
275 Controller.prototype.isKeyboardReady = false;
276
277
278 /**
279 * The auto repeat timer for backspace hold.
280 *
281 * @type {goog.async.Delay}
282 * @private
283 */
284 Controller.prototype.backspaceAutoRepeat_;
285
286
287 /**
288 * The active keyboard code.
289 *
290 * @type {string}
291 * @private
292 */
293 Controller.prototype.currentKeyset_ = '';
294
295
296 /**
297 * The current input method id.
298 *
299 * @private {string}
300 */
301 Controller.prototype.currentInputmethod_ = '';
302
303
304 /**
305 * The operations on candidates.
306 *
307 * @enum {number}
308 */
309 Controller.CandidatesOperation = {
310 NONE: 0,
311 EXPAND: 1,
312 SHRINK: 2
313 };
314
315
316 /**
317 * The active language code.
318 *
319 * @type {string}
320 * @private
321 */
322 Controller.prototype.lang_;
323
324
325 /**
326 * The password keyset.
327 *
328 * @private {string}
329 */
330 Controller.prototype.passwordKeyset_ = '';
331
332
333 /**
334 * The soft key map, because key configuration is loaded before layout,
335 * controller needs this varaible to save it and hook into keyboard view.
336 *
337 * @type {!Array.<!content.SoftKey>}
338 * @private
339 */
340 Controller.prototype.softKeyList_;
341
342
343 /**
344 * The mapping from soft key id to soft key view id.
345 *
346 * @type {!Object.<string, string>}
347 * @private
348 */
349 Controller.prototype.mapping_;
350
351
352 /**
353 * The dead key.
354 *
355 * @type {string}
356 * @private
357 */
358 Controller.prototype.deadKey_ = '';
359
360
361 /**
362 * The input tool name.
363 *
364 * @type {string}
365 * @private
366 */
367 Controller.prototype.title_;
368
369
370 /**
371 * Registers event handlers.
372 * @private
373 */
374 Controller.prototype.registerEventHandler_ = function() {
375 this.handler_.
376 listen(this.model_,
377 EventType.LAYOUT_LOADED,
378 this.onLayoutLoaded_).
379 listen(this.model_,
380 EventType.CONFIG_LOADED,
381 this.onConfigLoaded_).
382 listen(this.m17nModel_,
383 EventType.CONFIG_LOADED,
384 this.onConfigLoaded_).
385 listen(this.pointerHandler_, [
386 EventType.LONG_PRESS,
387 EventType.CLICK,
388 EventType.DOUBLE_CLICK,
389 EventType.DOUBLE_CLICK_END,
390 EventType.POINTER_UP,
391 EventType.POINTER_DOWN,
392 EventType.POINTER_OVER,
393 EventType.POINTER_OUT,
394 EventType.SWIPE
395 ], this.onPointerEvent_).
396 listen(window, goog.events.EventType.RESIZE, this.resize).
397 listen(this.adapter_,
398 i18n.input.chrome.inputview.events.EventType.
399 SURROUNDING_TEXT_CHANGED,
400 this.onSurroundingTextChanged_).
401 listen(this.adapter_,
402 i18n.input.chrome.DataSource.EventType.CANDIDATES_BACK,
403 this.onCandidatesBack_).
404 listen(this.adapter_,
405 i18n.input.chrome.inputview.events.EventType.CONTEXT_FOCUS,
406 this.onContextFocus_).
407 listen(this.adapter_,
408 i18n.input.chrome.inputview.events.EventType.CONTEXT_BLUR,
409 this.onContextBlur_).
410 listen(this.adapter_,
411 i18n.input.chrome.inputview.events.EventType.VISIBILITY_CHANGE,
412 this.onVisibilityChange_).
413 listen(this.adapter_,
414 i18n.input.chrome.inputview.events.EventType.SETTINGS_READY,
415 this.onSettingsReady_).
416 listen(this.adapter_,
417 i18n.input.chrome.message.Type.UPDATE_SETTINGS,
418 this.onUpdateSettings_);
419 };
420
421
422 /**
423 * Callback for updating settings.
424 *
425 * @param {!i18n.input.chrome.message.Event} e .
426 * @private
427 */
428 Controller.prototype.onUpdateSettings_ = function(e) {
429 var settings = this.model_.settings;
430 if (goog.isDef(e.msg['autoSpace'])) {
431 settings.autoSpace = e.msg['autoSpace'];
432 }
433 if (goog.isDef(e.msg['autoCapital'])) {
434 settings.autoCapital = e.msg['autoCapital'];
435 }
436 if (goog.isDef(e.msg['candidatesNavigation'])) {
437 settings.candidatesNavigation = e.msg['candidatesNavigation'];
438 }
439 if (goog.isDef(e.msg['supportCompact'])) {
440 settings.supportCompact = e.msg['supportCompact'];
441 }
442 if (goog.isDef(e.msg[Name.KEYSET])) {
443 this.contextTypeToKeysetMap_[this.currentInputMethod_][
444 ContextType.DEFAULT] = e.msg[Name.KEYSET];
445 }
446 if (goog.isDef(e.msg['enableLongPress'])) {
447 settings.enableLongPress = e.msg['enableLongPress'];
448 }
449 if (goog.isDef(e.msg['doubleSpacePeriod'])) {
450 settings.doubleSpacePeriod = e.msg['doubleSpacePeriod'];
451 }
452 if (goog.isDef(e.msg['soundOnKeypress'])) {
453 settings.soundOnKeypress = e.msg['soundOnKeypress'];
454 this.soundController_.setEnabled(settings.soundOnKeypress);
455 }
456 this.perfTracker_.tick(PerfTracker.TickName.BACKGROUND_SETTINGS_FETCHED);
457 var isPassword = this.adapter_.isPasswordBox();
458 this.switchToKeySet(this.getActiveKeyset_());
459 };
460
461
462 /**
463 * Callback for setting ready.
464 *
465 * @private
466 */
467 Controller.prototype.onSettingsReady_ = function() {
468 if (this.isSettingReady) {
469 return;
470 }
471
472 this.isSettingReady = true;
473 var keysetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
474 if (this.adapter_.isA11yMode) {
475 keysetMap[ContextType.PASSWORD] = keysetMap[ContextType.DEFAULT] =
476 util.getConfigName(keysetMap[ContextType.DEFAULT]);
477 } else {
478 var preferredKeyset = /** @type {string} */ (this.model_.settings.
479 getPreference(util.getConfigName(
480 keysetMap[ContextType.DEFAULT])));
481 if (preferredKeyset) {
482 keysetMap[ContextType.PASSWORD] = keysetMap[ContextType.DEFAULT] =
483 preferredKeyset;
484 }
485 }
486 this.maybeCreateViews_();
487 this.switchToKeySet(this.getActiveKeyset_());
488 };
489
490
491 /**
492 * Gets the data for spatial module.
493 *
494 * @param {!content.SoftKey} key .
495 * @param {number} x The x-offset of the touch point.
496 * @param {number} y The y-offset of the touch point.
497 * @return {!Object} .
498 * @private
499 */
500 Controller.prototype.getSpatialData_ = function(key, x, y) {
501 var items = [];
502 items.push([this.getKeyContent_(key), key.estimator.estimateInLogSpace(x, y)
503 ]);
504 for (var i = 0; i < key.nearbyKeys.length; i++) {
505 var nearByKey = key.nearbyKeys[i];
506 var content = this.getKeyContent_(nearByKey);
507 if (content && util.REGEX_LANGUAGE_MODEL_CHARACTERS.test(content)) {
508 items.push([content, nearByKey.estimator.estimateInLogSpace(x, y)]);
509 }
510 }
511 goog.array.sort(items, function(item1, item2) {
512 return item1[1] - item2[1];
513 });
514 var sources = items.map(function(item) {
515 return item[0].toLowerCase();
516 });
517 var possibilities = items.map(function(item) {
518 return item[1];
519 });
520 return {
521 'sources': sources,
522 'possibilities': possibilities
523 };
524 };
525
526
527 /**
528 * Gets the key content.
529 *
530 * @param {!content.SoftKey} key .
531 * @return {string} .
532 * @private
533 */
534 Controller.prototype.getKeyContent_ = function(key) {
535 if (key.type == i18n.input.chrome.inputview.elements.ElementType.
536 CHARACTER_KEY) {
537 key = /** @type {!content.CharacterKey} */ (key);
538 return key.getActiveCharacter();
539 }
540 if (key.type == i18n.input.chrome.inputview.elements.ElementType.
541 COMPACT_KEY) {
542 key = /** @type {!content.FunctionalKey} */ (key);
543 return key.text;
544 }
545 return '';
546 };
547
548
549 /**
550 * Callback for pointer event.
551 *
552 * @param {!i18n.input.chrome.inputview.events.PointerEvent} e .
553 * @private
554 */
555 Controller.prototype.onPointerEvent_ = function(e) {
556 if ((this.adapter_.isChromeVoxOn || !this.model_.settings.enableLongPress) &&
557 e.type == EventType.LONG_PRESS) {
558 return;
559 }
560
561 if (e.view) {
562 this.handlePointerAction_(e.view, e);
563 } else {
564 var tabbableKeysets = [
565 Controller.HANDWRITING_VIEW_CODE_,
566 Controller.EMOJI_VIEW_CODE_];
567 if (goog.array.contains(tabbableKeysets, this.currentKeyset_)) {
568 this.resetAll_();
569 this.switchToKeySet(this.container_.currentKeysetView.fromKeyset);
570 }
571 }
572 };
573
574
575 /**
576 * Handles the swipe action.
577 *
578 * @param {!i18n.input.chrome.inputview.elements.Element} view The view, for
579 * swipe event, the view would be the soft key which starts the swipe.
580 * @param {!i18n.input.chrome.inputview.events.SwipeEvent} e The swipe event.
581 * @private
582 */
583 Controller.prototype.handleSwipeAction_ = function(view, e) {
584 var direction = e.direction;
585 if (this.container_.altDataView.isVisible()) {
586 this.container_.altDataView.highlightItem(e.x, e.y);
587 return;
588 }
589
590 if (view.type == ElementType.CHARACTER_KEY) {
591 view = /** @type {!content.CharacterKey} */ (view);
592 if (direction & i18n.input.chrome.inputview.SwipeDirection.UP ||
593 direction & i18n.input.chrome.inputview.SwipeDirection.DOWN) {
594 var ch = view.getCharacterByGesture(!!(direction &
595 i18n.input.chrome.inputview.SwipeDirection.UP));
596 if (ch) {
597 view.flickerredCharacter = ch;
598 }
599 }
600 }
601
602 if (view.type == ElementType.COMPACT_KEY) {
603 view = /** @type {!content.CompactKey} */ (view);
604 if ((direction & i18n.input.chrome.inputview.SwipeDirection.UP) &&
605 view.hintText) {
606 view.flickerredCharacter = view.hintText;
607 }
608 }
609 };
610
611
612 /**
613 * Execute a command.
614 *
615 * @param {!i18n.input.chrome.inputview.elements.content.MenuView.Command}
616 * command The command that about to be executed.
617 * @param {string=} opt_arg The optional command argument.
618 * @private
619 */
620 Controller.prototype.executeCommand_ = function(command, opt_arg) {
621 var CommandEnum = MenuView.Command;
622 switch (command) {
623 case CommandEnum.SWITCH_IME:
624 var inputMethodId = opt_arg;
625 if (inputMethodId) {
626 this.adapter_.switchToInputMethod(inputMethodId);
627 }
628 break;
629
630 case CommandEnum.SWITCH_KEYSET:
631 var keyset = opt_arg;
632 if (keyset) {
633 this.statistics_.recordSwitch(keyset);
634 this.switchToKeySet(keyset);
635 }
636 break;
637 case CommandEnum.OPEN_EMOJI:
638 this.switchToKeySet(Controller.EMOJI_VIEW_CODE_);
639 break;
640
641 case CommandEnum.OPEN_HANDWRITING:
642 // TODO: remember handwriting keyset.
643 this.switchToKeySet(Controller.HANDWRITING_VIEW_CODE_);
644 break;
645
646 case CommandEnum.OPEN_SETTING:
647 if (window.inputview) {
648 inputview.openSettings();
649 }
650 break;
651 }
652 };
653
654
655 /**
656 * Handles the pointer action.
657 *
658 * @param {!i18n.input.chrome.inputview.elements.Element} view The view.
659 * @param {!i18n.input.chrome.inputview.events.PointerEvent} e .
660 * @private
661 */
662 Controller.prototype.handlePointerAction_ = function(view, e) {
663 if (e.type == i18n.input.chrome.inputview.events.EventType.SWIPE) {
664 e = /** @type {!i18n.input.chrome.inputview.events.SwipeEvent} */ (e);
665 this.handleSwipeAction_(view, e);
666 }
667 switch (view.type) {
668 case ElementType.BACK_BUTTON:
669 if (e.type == EventType.POINTER_UP) {
670 this.switchToKeySet(this.container_.currentKeysetView.fromKeyset);
671 }
672 return;
673 case ElementType.EXPAND_CANDIDATES:
674 if (e.type == EventType.POINTER_UP) {
675 this.showCandidates_(this.candidatesInfo_.source,
676 this.candidatesInfo_.candidates,
677 Controller.CandidatesOperation.EXPAND);
678 }
679 return;
680 case ElementType.SHRINK_CANDIDATES:
681 if (e.type == EventType.POINTER_UP) {
682 this.showCandidates_(this.candidatesInfo_.source,
683 this.candidatesInfo_.candidates,
684 Controller.CandidatesOperation.SHRINK);
685 }
686 return;
687 case ElementType.CANDIDATE:
688 view = /** @type {!Candidate} */ (view);
689 if (e.type == EventType.POINTER_UP) {
690 if (view.candidateType == CandidateType.CANDIDATE) {
691 this.adapter_.selectCandidate(view.candidate);
692 } else if (view.candidateType == CandidateType.NUMBER) {
693 this.adapter_.commitText(view.candidate[Name.CANDIDATE]);
694 }
695 this.container_.cleanStroke();
696 }
697 if (e.type == EventType.POINTER_OUT || e.type == EventType.POINTER_UP) {
698 view.setHighlighted(false);
699 } else if (e.type == EventType.POINTER_DOWN ||
700 e.type == EventType.POINTER_OVER) {
701 view.setHighlighted(true);
702 }
703 return;
704
705 case ElementType.ALTDATA_VIEW:
706 view = /** @type {!content.AltDataView} */ (view);
707 if (e.type == EventType.POINTER_DOWN &&
708 e.target == view.getCoverElement()) {
709 view.hide();
710 } else if (e.type == EventType.POINTER_UP) {
711 var ch = view.getHighlightedCharacter();
712 this.adapter_.sendKeyDownAndUpEvent(ch, view.triggeredBy.id,
713 view.triggeredBy.keyCode,
714 {'sources': [ch.toLowerCase()], 'possibilities': [1]});
715 view.hide();
716 this.clearUnstickyState_();
717 }
718 return;
719
720 case ElementType.MENU_ITEM:
721 view = /** @type {!content.MenuItem} */ (view);
722 if (e.type == EventType.CLICK) {
723 this.resetAll_();
724 this.executeCommand_.apply(this, view.getCommand());
725 this.container_.menuView.hide();
726 }
727 view.setHighlighted(e.type == EventType.POINTER_DOWN ||
728 e.type == EventType.POINTER_OVER);
729 // TODO: Add chrome vox support.
730 return;
731
732 case ElementType.MENU_VIEW:
733 view = /** @type {!MenuView} */ (view);
734
735 if (e.type == EventType.POINTER_DOWN &&
736 e.target == view.getCoverElement()) {
737 view.hide();
738 }
739 return;
740
741 case ElementType.EMOJI_KEY:
742 if (e.type == EventType.POINTER_UP) {
743 if (!this.container_.currentKeysetView.isDragging && view.text != '') {
744 this.adapter_.commitText(view.text);
745 }
746 }
747 return;
748
749 case ElementType.HWT_PRIVACY_GOT_IT:
750 this.adapter_.sendHwtPrivacyConfirmMessage();
751 return;
752
753 case ElementType.SOFT_KEY_VIEW:
754 // Delegates the events on the soft key view to its soft key.
755 view = /** @type {!i18n.input.chrome.inputview.elements.layout.
756 SoftKeyView} */ (view);
757 if (!view.softKey) {
758 return;
759 }
760 view = view.softKey;
761 }
762
763 if (view.type != ElementType.MODIFIER_KEY &&
764 !this.container_.altDataView.isVisible() &&
765 !this.container_.menuView.isVisible()) {
766 // The highlight of the modifier key is depending on the state instead
767 // of the key down or up.
768 if (e.type == EventType.POINTER_OVER || e.type == EventType.POINTER_DOWN ||
769 e.type == EventType.DOUBLE_CLICK) {
770 view.setHighlighted(true);
771 } else if (e.type == EventType.POINTER_OUT ||
772 e.type == EventType.POINTER_UP ||
773 e.type == EventType.DOUBLE_CLICK_END) {
774 view.setHighlighted(false);
775 }
776 }
777 this.handlePointerEventForSoftKey_(
778 /** @type {!content.SoftKey} */ (view), e);
779 this.updateContextModifierState_();
780 };
781
782
783 /**
784 * Handles softkey of the pointer action.
785 *
786 * @param {!content.SoftKey} softKey .
787 * @param {!i18n.input.chrome.inputview.events.PointerEvent} e .
788 * @private
789 */
790 Controller.prototype.handlePointerEventForSoftKey_ = function(softKey, e) {
791 var key;
792 switch (softKey.type) {
793 case ElementType.CANDIDATES_PAGE_UP:
794 if (e.type == EventType.POINTER_UP) {
795 this.container_.expandedCandidateView.pageUp();
796 }
797 break;
798 case ElementType.CANDIDATES_PAGE_DOWN:
799 if (e.type == EventType.POINTER_UP) {
800 this.container_.expandedCandidateView.pageDown();
801 }
802 break;
803 case ElementType.CHARACTER_KEY:
804 key = /** @type {!content.CharacterKey} */ (softKey);
805 if (e.type == EventType.LONG_PRESS) {
806 this.container_.altDataView.show(
807 key, goog.i18n.bidi.isRtlLanguage(this.languageCode_));
808 } else if (e.type == EventType.POINTER_UP) {
809 this.model_.stateManager.triggerChording();
810 var ch = key.getActiveCharacter();
811 this.adapter_.sendKeyDownAndUpEvent(ch, key.id, key.keyCode,
812 this.getSpatialData_(key, e.x, e.y));
813 this.clearUnstickyState_();
814 key.flickerredCharacter = '';
815 }
816 break;
817
818 case ElementType.MODIFIER_KEY:
819 key = /** @type {!content.ModifierKey} */ (softKey);
820 var isStateEnabled = this.model_.stateManager.hasState(key.toState);
821 var isChording = this.model_.stateManager.isChording(key.toState);
822 if (e.type == EventType.POINTER_DOWN) {
823 this.changeState_(key.toState, !isStateEnabled, true);
824 this.model_.stateManager.setKeyDown(key.toState, true);
825 } else if (e.type == EventType.POINTER_UP || e.type == EventType.
826 POINTER_OUT) {
827 if (isChording) {
828 this.changeState_(key.toState, false, false);
829 } else if (key.toState != StateType.CAPSLOCK &&
830 this.model_.stateManager.isKeyDown(key.toState)) {
831 this.changeState_(key.toState, isStateEnabled, false);
832 }
833 this.model_.stateManager.setKeyDown(key.toState, false);
834 } else if (e.type == EventType.DOUBLE_CLICK) {
835 this.changeState_(key.toState, isStateEnabled, true);
836 } else if (e.type == EventType.LONG_PRESS) {
837 if (!isChording) {
838 this.changeState_(key.toState, true, true);
839 this.model_.stateManager.setKeyDown(key.toState, false);
840 }
841 }
842 break;
843
844 case ElementType.BACKSPACE_KEY:
845 key = /** @type {!content.FunctionalKey} */ (softKey);
846 if (e.type == EventType.POINTER_DOWN) {
847 this.backspaceTick_();
848 } else if (e.type == EventType.POINTER_UP || e.type == EventType.
849 POINTER_OUT) {
850 this.stopBackspaceAutoRepeat_();
851 this.adapter_.sendKeyUpEvent('\u0008', KeyCodes.BACKSPACE);
852 }
853 break;
854
855 case ElementType.TAB_KEY:
856 key = /** @type {!content.FunctionalKey} */ (softKey);
857 if (e.type == EventType.POINTER_DOWN) {
858 this.adapter_.sendKeyDownEvent('\u0009', KeyCodes.TAB);
859 } else if (e.type == EventType.POINTER_UP) {
860 this.adapter_.sendKeyUpEvent('\u0009', KeyCodes.TAB);
861 }
862 break;
863
864 case ElementType.ENTER_KEY:
865 key = /** @type {!content.FunctionalKey} */ (softKey);
866 if (e.type == EventType.POINTER_DOWN) {
867 this.adapter_.sendKeyDownEvent('\u000D', KeyCodes.ENTER);
868 } else if (e.type == EventType.POINTER_UP) {
869 this.adapter_.sendKeyUpEvent('\u000D', KeyCodes.ENTER);
870 }
871 break;
872
873 case ElementType.ARROW_UP:
874 if (e.type == EventType.POINTER_DOWN) {
875 this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_UP);
876 } else if (e.type == EventType.POINTER_UP) {
877 this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_UP);
878 }
879 break;
880
881 case ElementType.ARROW_DOWN:
882 if (e.type == EventType.POINTER_DOWN) {
883 this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_DOWN);
884 } else if (e.type == EventType.POINTER_UP) {
885 this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_DOWN);
886 }
887 break;
888
889 case ElementType.ARROW_LEFT:
890 if (e.type == EventType.POINTER_DOWN) {
891 this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_LEFT);
892 } else if (e.type == EventType.POINTER_UP) {
893 this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_LEFT);
894 }
895 break;
896
897 case ElementType.ARROW_RIGHT:
898 if (e.type == EventType.POINTER_DOWN) {
899 this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_RIGHT);
900 } else if (e.type == EventType.POINTER_UP) {
901 this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_RIGHT);
902 }
903 break;
904 case ElementType.EN_SWITCHER:
905 if (e.type == EventType.POINTER_UP) {
906 key = /** @type {!content.EnSwitcherKey} */ (softKey);
907 this.adapter_.toggleLanguageState(this.model_.stateManager.isEnMode);
908 this.model_.stateManager.isEnMode = !this.model_.stateManager.isEnMode;
909 key.update();
910 }
911 break;
912 case ElementType.SPACE_KEY:
913 key = /** @type {!content.SpaceKey} */ (softKey);
914 var doubleSpacePeriod = this.model_.settings.doubleSpacePeriod;
915 if (e.type == EventType.POINTER_UP || (!doubleSpacePeriod && e.type ==
916 EventType.DOUBLE_CLICK_END)) {
917 this.adapter_.sendKeyDownAndUpEvent(key.getCharacter(),
918 KeyCodes.SPACE);
919 this.clearUnstickyState_();
920 } else if (e.type == EventType.DOUBLE_CLICK && doubleSpacePeriod) {
921 this.adapter_.doubleClickOnSpaceKey();
922 }
923 break;
924
925 case ElementType.SWITCHER_KEY:
926 key = /** @type {!content.SwitcherKey} */ (softKey);
927 if (e.type == EventType.POINTER_UP) {
928 this.statistics_.recordSwitch(key.toKeyset);
929 if (this.isSubKeyset_(key.toKeyset, this.currentKeyset_)) {
930 this.model_.stateManager.reset();
931 this.container_.update();
932 this.updateContextModifierState_();
933 this.container_.menuView.hide();
934 } else {
935 this.resetAll_();
936 }
937 // Switch to the specific keyboard.
938 this.switchToKeySet(key.toKeyset);
939 if (key.record) {
940 this.model_.settings.savePreference(
941 util.getConfigName(key.toKeyset),
942 key.toKeyset);
943 }
944 }
945 break;
946
947 case ElementType.COMPACT_KEY:
948 key = /** @type {!content.CompactKey} */ (softKey);
949 if (e.type == EventType.LONG_PRESS) {
950 this.container_.altDataView.show(
951 key, goog.i18n.bidi.isRtlLanguage(this.languageCode_));
952 } else if (e.type == EventType.POINTER_UP) {
953 this.model_.stateManager.triggerChording();
954 this.adapter_.sendKeyDownAndUpEvent(key.getActiveCharacter(), '', 0,
955 this.getSpatialData_(key, e.x, e.y));
956 this.clearUnstickyState_();
957 key.flickerredCharacter = '';
958 }
959 break;
960
961 case ElementType.HIDE_KEYBOARD_KEY:
962 if (e.type == EventType.POINTER_UP) {
963 this.adapter_.hideKeyboard();
964 }
965 break;
966
967 case ElementType.MENU_KEY:
968 key = /** @type {!content.MenuKey} */ (softKey);
969 if (e.type == EventType.POINTER_DOWN) {
970 var isCompact = this.currentKeyset_.indexOf('compact') != -1;
971 var remappedKeyset = this.getRemappedKeyset_(this.currentKeyset_);
972 var keysetData = this.keysetDataMap_[remappedKeyset];
973 var enableCompact = !this.adapter_.isA11yMode &&
974 !!keysetData[SpecNodeName.HAS_COMPACT_KEYBOARD] &&
975 this.model_.settings.supportCompact;
976 var self = this;
977 var currentKeyset = this.currentKeyset_;
978 var hasHwt = !this.adapter_.isPasswordBox() &&
979 !Controller.DISABLE_HWT && goog.object.contains(
980 InputToolCode, this.getHwtInputToolCode_());
981 var enableSettings = this.shouldEnableSettings() &&
982 window.inputview && inputview.openSettings;
983 this.adapter_.getInputMethods(function(inputMethods) {
984 this.container_.menuView.show(key, currentKeyset, isCompact,
985 enableCompact, this.currentInputMethod_, inputMethods, hasHwt,
986 enableSettings, this.adapter_.isExperimental);
987 }.bind(this));
988 }
989 break;
990
991 case ElementType.GLOBE_KEY:
992 if (e.type == EventType.POINTER_UP) {
993 this.adapter_.clearModifierStates();
994 this.adapter_.setModifierState(
995 i18n.input.chrome.inputview.StateType.CTRL, true);
996 this.adapter_.sendKeyDownAndUpEvent(' ', KeyCodes.SPACE, 0x20);
997 this.adapter_.setModifierState(
998 i18n.input.chrome.inputview.StateType.CTRL, false);
999 }
1000 break;
1001 case ElementType.IME_SWITCH:
1002 key = /** @type {!content.FunctionalKey} */ (softKey);
1003 this.adapter_.sendKeyDownAndUpEvent('', key.id);
1004 break;
1005 }
1006 // Play key sound on pointer up.
1007 if (e.type == EventType.POINTER_UP)
1008 this.soundController_.onKeyUp(softKey.type);
1009 };
1010
1011
1012 /**
1013 * Clears unsticky state.
1014 *
1015 * @private
1016 */
1017 Controller.prototype.clearUnstickyState_ = function() {
1018 if (this.model_.stateManager.hasUnStickyState()) {
1019 for (var key in StateType) {
1020 var value = StateType[key];
1021 if (this.model_.stateManager.hasState(value) &&
1022 !this.model_.stateManager.isSticky(value)) {
1023 this.changeState_(value, false, false);
1024 }
1025 }
1026 }
1027 this.container_.update();
1028 };
1029
1030
1031 /**
1032 * Stops the auto-repeat for backspace.
1033 *
1034 * @private
1035 */
1036 Controller.prototype.stopBackspaceAutoRepeat_ = function() {
1037 goog.dispose(this.backspaceAutoRepeat_);
1038 this.backspaceAutoRepeat_ = null;
1039 this.adapter_.sendKeyUpEvent('\u0008', KeyCodes.BACKSPACE);
1040 this.backspaceRepeated_ = 0;
1041 };
1042
1043
1044 /**
1045 * The tick for the backspace key.
1046 *
1047 * @private
1048 */
1049 Controller.prototype.backspaceTick_ = function() {
1050 if (this.backspaceRepeated_ >= Controller.BACKSPACE_REPEAT_LIMIT_) {
1051 this.stopBackspaceAutoRepeat_();
1052 return;
1053 }
1054 this.backspaceRepeated_++;
1055 this.backspaceDown_();
1056 this.soundController_.onKeyRepeat(ElementType.BACKSPACE_KEY);
1057
1058 if (this.backspaceAutoRepeat_) {
1059 this.backspaceAutoRepeat_.start(75);
1060 } else {
1061 this.backspaceAutoRepeat_ = new goog.async.Delay(
1062 goog.bind(this.backspaceTick_, this), 300);
1063 this.backspaceAutoRepeat_.start();
1064 }
1065 };
1066
1067
1068 /**
1069 * Callback for VISIBILITY_CHANGE.
1070 *
1071 * @private
1072 */
1073 Controller.prototype.onVisibilityChange_ = function() {
1074 if (!this.adapter_.isVisible) {
1075 this.resetAll_();
1076 }
1077 };
1078
1079
1080 /**
1081 * Resets the whole keyboard include clearing candidates,
1082 * reset modifier state, etc.
1083 *
1084 * @private
1085 */
1086 Controller.prototype.resetAll_ = function() {
1087 this.clearCandidates_();
1088 this.container_.candidateView.hideNumberRow();
1089 this.model_.stateManager.reset();
1090 this.container_.update();
1091 this.updateContextModifierState_();
1092 this.deadKey_ = '';
1093 this.resize();
1094 this.container_.expandedCandidateView.close();
1095 this.container_.menuView.hide();
1096 };
1097
1098
1099 /**
1100 * Callback when the context is changed.
1101 *
1102 * @private
1103 */
1104 Controller.prototype.onContextFocus_ = function() {
1105 this.resetAll_();
1106 this.switchToKeySet(this.getActiveKeyset_());
1107 };
1108
1109
1110 /**
1111 * Callback when surrounding text is changed.
1112 *
1113 * @param {!i18n.input.chrome.inputview.events.SurroundingTextChangedEvent} e .
1114 * @private
1115 */
1116 Controller.prototype.onSurroundingTextChanged_ = function(e) {
1117 if (!this.model_.settings.autoCapital || !e.text) {
1118 return;
1119 }
1120
1121 var isShiftEnabled = this.model_.stateManager.hasState(StateType.SHIFT);
1122 var needAutoCap = this.model_.settings.autoCapital &&
1123 util.needAutoCap(e.text);
1124 if (needAutoCap && !isShiftEnabled) {
1125 this.changeState_(StateType.SHIFT, true, false);
1126 this.shiftForAutoCapital_ = true;
1127 } else if (!needAutoCap && this.shiftForAutoCapital_) {
1128 this.changeState_(StateType.SHIFT, false, false);
1129 }
1130 };
1131
1132
1133 /**
1134 * Callback for Context blurs.
1135 *
1136 * @private
1137 */
1138 Controller.prototype.onContextBlur_ = function() {
1139 this.clearCandidates_();
1140 this.deadKey_ = '';
1141 this.container_.menuView.hide();
1142 };
1143
1144
1145 /**
1146 * Backspace key is down.
1147 *
1148 * @private
1149 */
1150 Controller.prototype.backspaceDown_ = function() {
1151 if (this.container_.hasStrokesOnCanvas()) {
1152 this.clearCandidates_();
1153 this.container_.cleanStroke();
1154 } else {
1155 this.adapter_.sendKeyDownEvent('\u0008', KeyCodes.BACKSPACE);
1156 }
1157 };
1158
1159
1160 /**
1161 * Callback for state change.
1162 *
1163 * @param {StateType} stateType The state type.
1164 * @param {boolean} enable True to enable the state.
1165 * @param {boolean} isSticky True to make the state sticky.
1166 * @private
1167 */
1168 Controller.prototype.changeState_ = function(stateType, enable, isSticky) {
1169 if (stateType == StateType.ALTGR) {
1170 var code = KeyCodes.ALT_RIGHT;
1171 if (enable) {
1172 this.adapter_.sendKeyDownEvent('', code);
1173 } else {
1174 this.adapter_.sendKeyUpEvent('', code);
1175 }
1176 }
1177 if (stateType == StateType.SHIFT) {
1178 this.shiftForAutoCapital_ = false;
1179 }
1180 var isEnabledBefore = this.model_.stateManager.hasState(stateType);
1181 var isStickyBefore = this.model_.stateManager.isSticky(stateType);
1182 this.model_.stateManager.setState(stateType, enable);
1183 this.model_.stateManager.setSticky(stateType, isSticky);
1184 if (isEnabledBefore != enable || isStickyBefore != isSticky) {
1185 this.container_.update();
1186 }
1187 };
1188
1189
1190 /**
1191 * Updates the modifier state for context.
1192 *
1193 * @private
1194 */
1195 Controller.prototype.updateContextModifierState_ = function() {
1196 var stateManager = this.model_.stateManager;
1197 this.adapter_.setModifierState(StateType.ALT,
1198 stateManager.hasState(StateType.ALT));
1199 this.adapter_.setModifierState(StateType.CTRL,
1200 stateManager.hasState(StateType.CTRL));
1201 this.adapter_.setModifierState(StateType.CAPSLOCK,
1202 stateManager.hasState(StateType.CAPSLOCK));
1203 if (!this.shiftForAutoCapital_) {
1204 // If shift key is automatically on because of feature - autoCapital,
1205 // Don't set modifier state to adapter.
1206 this.adapter_.setModifierState(StateType.SHIFT,
1207 stateManager.hasState(StateType.SHIFT));
1208 }
1209 };
1210
1211
1212 /**
1213 * Callback for AUTO-COMPLETE event.
1214 *
1215 * @param {!i18n.input.chrome.DataSource.CandidatesBackEvent} e .
1216 * @private
1217 */
1218 Controller.prototype.onCandidatesBack_ = function(e) {
1219 this.candidatesInfo_ = new i18n.input.chrome.inputview.CandidatesInfo(
1220 e.source, e.candidates);
1221 this.showCandidates_(e.source, e.candidates, Controller.CandidatesOperation.
1222 NONE);
1223 };
1224
1225
1226 /**
1227 * Shows the candidates to the candidate view.
1228 *
1229 * @param {string} source The source text.
1230 * @param {!Array.<!Object>} candidates The candidate text list.
1231 * @param {Controller.CandidatesOperation} operation .
1232 * @private
1233 */
1234 Controller.prototype.showCandidates_ = function(source, candidates,
1235 operation) {
1236 var state = !!source ? ExpandedCandidateView.State.COMPLETION_CORRECTION :
1237 ExpandedCandidateView.State.PREDICTION;
1238 var expandView = this.container_.expandedCandidateView;
1239 var expand = false;
1240 if (operation == Controller.CandidatesOperation.NONE) {
1241 expand = expandView.state == state;
1242 } else {
1243 expand = operation == Controller.CandidatesOperation.EXPAND;
1244 }
1245
1246 if (candidates.length == 0) {
1247 this.clearCandidates_();
1248 expandView.state = ExpandedCandidateView.State.NONE;
1249 return;
1250 }
1251
1252 // The compact pinyin needs full candidates instead of three candidates.
1253 var isThreeCandidates = this.currentKeyset_.indexOf('compact') != -1 &&
1254 this.currentKeyset_.indexOf('pinyin-zh-CN') == -1;
1255 if (isThreeCandidates) {
1256 if (candidates.length > 1) {
1257 // Swap the first candidate and the second candidate.
1258 var tmp = candidates[0];
1259 candidates[0] = candidates[1];
1260 candidates[1] = tmp;
1261 }
1262 }
1263 var isHwt = Controller.HANDWRITING_VIEW_CODE_ == this.currentKeyset_;
1264 this.container_.candidateView.showCandidates(candidates, isThreeCandidates,
1265 this.model_.settings.candidatesNavigation && !isHwt);
1266
1267 if (expand) {
1268 expandView.state = state;
1269 this.container_.currentKeysetView.setVisible(false);
1270 expandView.showCandidates(candidates,
1271 this.container_.candidateView.candidateCount);
1272 this.container_.candidateView.switchToIcon(CandidateView.IconType.
1273 SHRINK_CANDIDATES, true);
1274 } else {
1275 expandView.state = ExpandedCandidateView.State.NONE;
1276 expandView.setVisible(false);
1277 this.container_.currentKeysetView.setVisible(true);
1278 }
1279 };
1280
1281
1282 /**
1283 * Clears candidates.
1284 *
1285 * @private
1286 */
1287 Controller.prototype.clearCandidates_ = function() {
1288 this.candidatesInfo_ = i18n.input.chrome.inputview.CandidatesInfo.getEmpty();
1289 this.container_.candidateView.clearCandidates();
1290 this.container_.expandedCandidateView.close();
1291 this.container_.expandedCandidateView.state = ExpandedCandidateView.State.
1292 NONE;
1293 if (this.container_.currentKeysetView) {
1294 this.container_.currentKeysetView.setVisible(true);
1295 }
1296 this.container_.candidateView.switchToIcon(CandidateView.IconType.BACK,
1297 Controller.HANDWRITING_VIEW_CODE_ == this.currentKeyset_);
1298 };
1299
1300
1301 /**
1302 * Callback when the layout is loaded.
1303 *
1304 * @param {!i18n.input.chrome.inputview.events.LayoutLoadedEvent} e The event.
1305 * @private
1306 */
1307 Controller.prototype.onLayoutLoaded_ = function(e) {
1308 var layoutID = e.data['layoutID'];
1309 this.layoutDataMap_[layoutID] = e.data;
1310 this.perfTracker_.tick(PerfTracker.TickName.LAYOUT_LOADED);
1311 this.maybeCreateViews_();
1312 };
1313
1314
1315 /**
1316 * Creates the whole view.
1317 *
1318 * @private
1319 */
1320 Controller.prototype.maybeCreateViews_ = function() {
1321 if (!this.isSettingReady) {
1322 return;
1323 }
1324
1325 for (var keyset in this.keysetDataMap_) {
1326 var keysetData = this.keysetDataMap_[keyset];
1327 var layoutId = keysetData[SpecNodeName.LAYOUT];
1328 var layoutData = this.layoutDataMap_[layoutId];
1329 if (!this.container_.keysetViewMap[keyset] && layoutData) {
1330 var conditions = {};
1331 conditions[ConditionName.SHOW_ALTGR] =
1332 keysetData[SpecNodeName.HAS_ALTGR_KEY];
1333
1334 conditions[ConditionName.SHOW_MENU] =
1335 keysetData[SpecNodeName.SHOW_MENU_KEY];
1336 // In symbol and more keysets, we want to show a symbol key in the globe
1337 // SoftKeyView. So this view should alway visible in the two keysets.
1338 // Currently, SHOW_MENU_KEY is false for the two keysets, so we use
1339 // !keysetData[SpecNodeName.SHOW_MENU_KEY] here.
1340 conditions[ConditionName.SHOW_GLOBE_OR_SYMBOL] =
1341 !keysetData[SpecNodeName.SHOW_MENU_KEY] ||
1342 this.adapter_.showGlobeKey;
1343 conditions[ConditionName.SHOW_EN_SWITCHER_KEY] = false;
1344
1345 // If the view for the keyboard code doesn't exist, and the layout
1346 // data is ready, then creates the view.
1347 this.container_.addKeysetView(keysetData, layoutData, keyset,
1348 this.languageCode_, this.model_, this.title_, conditions);
1349 this.perfTracker_.tick(PerfTracker.TickName.KEYBOARD_CREATED);
1350 }
1351 }
1352 this.switchToKeySet(this.getActiveKeyset_());
1353 };
1354
1355
1356 /**
1357 * Switch to a specific keyboard.
1358 *
1359 * @param {string} keyset The keyset name.
1360 */
1361 Controller.prototype.switchToKeySet = function(keyset) {
1362 if (!this.isSettingReady) {
1363 return;
1364 }
1365
1366 var lastKeysetView = this.container_.currentKeysetView;
1367 var ret = this.container_.switchToKeyset(this.getRemappedKeyset_(keyset),
1368 this.title_, this.adapter_.isPasswordBox(), this.adapter_.isA11yMode,
1369 keyset, this.currentKeyset_, this.languageCode_);
1370
1371 // Update the keyset of current context type.
1372 this.contextTypeToKeysetMap_[this.currentInputMethod_][
1373 this.adapter_.getContextType()] = keyset;
1374
1375 if (ret) {
1376 this.updateLanguageState_(this.currentKeyset_, keyset);
1377 // Deactivate the last keyset view instance.
1378 if (lastKeysetView) {
1379 lastKeysetView.deactivate(this.currentKeyset_);
1380 }
1381 this.currentKeyset_ = keyset;
1382
1383 this.resize(Controller.DEV);
1384 this.statistics_.setCurrentLayout(keyset);
1385 // Activate the current key set view instance.
1386 this.container_.currentKeysetView.activate(keyset);
1387 this.perfTracker_.tick(PerfTracker.TickName.KEYBOARD_SHOWN);
1388 this.perfTracker_.stop();
1389 } else {
1390 this.loadResource_(keyset);
1391 }
1392 };
1393
1394
1395 /**
1396 * Callback when the configuration is loaded.
1397 *
1398 * @param {!i18n.input.chrome.inputview.events.ConfigLoadedEvent} e The event.
1399 * @private
1400 */
1401 Controller.prototype.onConfigLoaded_ = function(e) {
1402 var data = e.data;
1403 var keyboardCode = data[i18n.input.chrome.inputview.SpecNodeName.ID];
1404 this.keysetDataMap_[keyboardCode] = data;
1405 this.perfTracker_.tick(PerfTracker.TickName.KEYSET_LOADED);
1406 var context = data[i18n.input.chrome.inputview.SpecNodeName.ON_CONTEXT];
1407 if (context && !this.adapter_.isA11yMode) {
1408 var keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
1409 if (!keySetMap) {
1410 keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_] = {};
1411 }
1412 keySetMap[context] = keyboardCode;
1413 }
1414
1415 var layoutId = data[i18n.input.chrome.inputview.SpecNodeName.LAYOUT];
1416 var layoutData = this.layoutDataMap_[layoutId];
1417 if (layoutData) {
1418 this.maybeCreateViews_();
1419 } else {
1420 this.model_.loadLayout(data[i18n.input.chrome.inputview.SpecNodeName.
1421 LAYOUT]);
1422 }
1423 };
1424
1425
1426 /**
1427 * Resizes the whole UI.
1428 *
1429 * @param {boolean=} opt_ignoreWindowResize .
1430 */
1431 Controller.prototype.resize = function(opt_ignoreWindowResize) {
1432 var height;
1433 var widthPercent;
1434 var candidateViewHeight;
1435 var isHorizontal = screen.width > screen.height;
1436 var isWideScreen = (Math.min(screen.width, screen.height) / Math.max(
1437 screen.width, screen.height)) < 0.6;
1438 this.model_.stateManager.covariance.update(isWideScreen, isHorizontal,
1439 this.adapter_.isA11yMode);
1440 if (this.adapter_.isA11yMode) {
1441 height = SizeSpec.A11Y_HEIGHT;
1442 widthPercent = screen.width > screen.height ? SizeSpec.A11Y_WIDTH_PERCENT.
1443 HORIZONTAL : SizeSpec.A11Y_WIDTH_PERCENT.VERTICAL;
1444 candidateViewHeight = SizeSpec.A11Y_CANDIDATE_VIEW_HEIGHT;
1445 } else {
1446 var keyset = this.keysetDataMap_[this.currentKeyset_];
1447 var layout = keyset && keyset[SpecNodeName.LAYOUT];
1448 var data = layout && this.layoutDataMap_[layout];
1449 var spec = data && data[SpecNodeName.WIDTH_PERCENT] ||
1450 SizeSpec.NON_A11Y_WIDTH_PERCENT;
1451 height = SizeSpec.NON_A11Y_HEIGHT;
1452 if (isHorizontal) {
1453 if (isWideScreen) {
1454 widthPercent = spec.HORIZONTAL_WIDE_SCREEN;
1455 } else {
1456 widthPercent = spec.HORIZONTAL;
1457 }
1458 } else {
1459 widthPercent = spec.VERTICAL;
1460 }
1461 candidateViewHeight = SizeSpec.NON_A11Y_CANDIDATE_VIEW_HEIGHT;
1462 }
1463
1464 var viewportSize = goog.dom.getViewportSize();
1465 if (viewportSize.height != height && !opt_ignoreWindowResize) {
1466 window.resizeTo(screen.width, height);
1467 return;
1468 }
1469
1470 this.container_.resize(screen.width, height, widthPercent,
1471 candidateViewHeight);
1472 if (this.container_.currentKeysetView) {
1473 this.isKeyboardReady = true;
1474 }
1475 };
1476
1477
1478 /**
1479 * Loads the resources, for currentKeyset, passwdKeyset, handwriting,
1480 * emoji, etc.
1481 *
1482 * @private
1483 */
1484 Controller.prototype.loadAllResources_ = function() {
1485 var keysetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
1486 goog.array.forEach([keysetMap[ContextType.DEFAULT],
1487 Controller.HANDWRITING_VIEW_CODE_,
1488 Controller.EMOJI_VIEW_CODE_,
1489 keysetMap[ContextType.PASSWORD]], function(keyset) {
1490 this.loadResource_(keyset);
1491 }, this);
1492 };
1493
1494
1495 /**
1496 * Gets the remapped keyset.
1497 *
1498 * @param {string} keyset .
1499 * @return {string} The remapped keyset.
1500 * @private
1501 */
1502 Controller.prototype.getRemappedKeyset_ = function(keyset) {
1503 if (goog.array.contains(util.KEYSETS_USE_US, keyset)) {
1504 return 'us';
1505 }
1506 return keyset;
1507 };
1508
1509
1510 /**
1511 * Loads a single resource.
1512 *
1513 * @param {string} keyset .
1514 * loaded.
1515 * @private
1516 */
1517 Controller.prototype.loadResource_ = function(keyset) {
1518 var remapped = this.getRemappedKeyset_(keyset);
1519 if (!this.keysetDataMap_[remapped]) {
1520 if (/^m17n:/.test(remapped)) {
1521 this.m17nModel_.loadConfig(remapped);
1522 } else {
1523 this.model_.loadConfig(remapped);
1524 }
1525 return;
1526 }
1527
1528 var layoutId = this.keysetDataMap_[remapped][SpecNodeName.LAYOUT];
1529 if (!this.layoutDataMap_[layoutId]) {
1530 this.model_.loadLayout(layoutId);
1531 return;
1532 }
1533 };
1534
1535
1536 /**
1537 * Sets the keyboard.
1538 *
1539 * @param {string} keyset The keyboard keyset.
1540 * @param {string} languageCode The language code for this keyboard.
1541 * @param {string} passwordLayout The layout for password box.
1542 * @param {string} title The title for this keyboard.
1543 */
1544 Controller.prototype.initialize = function(keyset, languageCode, passwordLayout,
1545 title) {
1546 this.perfTracker_.restart();
1547 this.adapter_.getCurrentInputMethod(function(currentInputMethod) {
1548 this.languageCode_ = languageCode;
1549 this.currentInputMethod_ = currentInputMethod;
1550 var keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
1551 if (!keySetMap) {
1552 keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_] = {};
1553 }
1554 keySetMap[ContextType.PASSWORD] = passwordLayout;
1555 keySetMap[ContextType.DEFAULT] = keyset;
1556
1557 this.title_ = title;
1558 this.isSettingReady = false;
1559 this.model_.settings = new i18n.input.chrome.inputview.Settings();
1560 this.adapter_.initialize(languageCode ? languageCode.split('-')[0] : '');
1561 this.loadAllResources_();
1562 this.switchToKeySet(this.getActiveKeyset_());
1563
1564 // Set language attribute and font of body.
1565 document.body.setAttribute('lang', this.languageCode_);
1566 goog.dom.classlist.add(document.body, Css.FONT);
1567 }.bind(this));
1568 };
1569
1570
1571 /** @override */
1572 Controller.prototype.disposeInternal = function() {
1573 goog.dispose(this.container_);
1574 goog.dispose(this.adapter_);
1575 goog.dispose(this.handler_);
1576 goog.dispose(this.soundController_);
1577
1578 goog.base(this, 'disposeInternal');
1579 };
1580
1581
1582 /**
1583 * Gets the handwriting Input Tool code of current language code.
1584 *
1585 * @return {string} The handwriting Input Tool code.
1586 * @private
1587 */
1588 Controller.prototype.getHwtInputToolCode_ = function() {
1589 return this.languageCode_.split(/_|-/)[0] +
1590 Controller.HANDWRITING_CODE_SUFFIX_;
1591 };
1592
1593
1594 /**
1595 * True to enable settings link.
1596 *
1597 * @return {boolean} .
1598 */
1599 Controller.prototype.shouldEnableSettings = function() {
1600 return !this.adapter_.screen || this.adapter_.screen == 'normal';
1601 };
1602
1603
1604 /**
1605 * Gets the active keyset, if there is a keyset to switch, return it.
1606 * otherwise if it's a password box, return the password keyset,
1607 * otherwise return the current keyset.
1608 *
1609 * @return {string} .
1610 * @private
1611 */
1612 Controller.prototype.getActiveKeyset_ = function() {
1613 var keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
1614 return keySetMap[this.adapter_.getContextType()] ||
1615 keySetMap[ContextType.DEFAULT];
1616 };
1617
1618
1619 /**
1620 * True if keysetB is the sub keyset of keysetA.
1621 *
1622 * @param {string} keysetA .
1623 * @param {string} keysetB .
1624 * @return {boolean} .
1625 * @private
1626 */
1627 Controller.prototype.isSubKeyset_ = function(keysetA, keysetB) {
1628 var segmentsA = keysetA.split('.');
1629 var segmentsB = keysetB.split('.');
1630 return segmentsA.length >= 2 && segmentsB.length >= 2 &&
1631 segmentsA[0] == segmentsB[0] && segmentsA[1] == segmentsB[1];
1632 };
1633
1634
1635 /**
1636 * Updates the compact pinyin to set the inputcode for english and pinyin.
1637 *
1638 * @param {string} fromRawKeyset .
1639 * @param {string} toRawKeyset .
1640 * @private
1641 */
1642 Controller.prototype.updateLanguageState_ =
1643 function(fromRawKeyset, toRawKeyset) {
1644 if (fromRawKeyset == 'pinyin-zh-CN.en.compact.qwerty' &&
1645 toRawKeyset.indexOf('en.compact') == -1) {
1646 this.adapter_.toggleLanguageState(true);
1647 } else if (fromRawKeyset.indexOf('en.compact') == -1 &&
1648 toRawKeyset == 'pinyin-zh-CN.en.compact.qwerty') {
1649 this.adapter_.toggleLanguageState(false);
1650 } else if (goog.array.contains(
1651 i18n.input.chrome.inputview.util.KEYSETS_HAVE_EN_SWTICHER,
1652 toRawKeyset)) {
1653 this.adapter_.toggleLanguageState(true);
1654 this.model_.stateManager.isEnMode = false;
1655 this.container_.currentKeysetView.update();
1656 }
1657 };
1658 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698