OLD | NEW |
(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 |
OLD | NEW |