Codebase list mozc / 7306a9d chrome / skk / ime.js
7306a9d

Tree @7306a9d (Download .tar.gz)

ime.js @7306a9draw · history · blame

// Copyright 2010-2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/**
 * @fileoverview This file contains skk.IME class implementation.
 */

'use strict';

/**
 * Root namespace.
 */
var skk = {};

/**
 * Array holding japanese vowels.
 * @const
 * @type Array.<string>
 */
skk.VOWELS = ['a', 'i', 'u', 'e', 'o'];

/**
 * Number of candidates listed at once.
 * @const
 * @type number
 */
skk.NUM_CANDIDATES = 7;

/**
 * Key of LocalStorage used for storing conversion history.
 * @const
 * @type string
 */
skk.HISTORY_STORAGE_KEY = 'conversion_history';

/**
 * Does nothing but reports error. This is useful as placeholder for interface.
 * @type function
 */
skk.abstractMethod = function() {
  console.error('skk.abstractMethod: Unimplemented abstract method');
};

/**
 * Main class. Receives IME events and delegates most of them to skk.mode.*.
 * @constructor
 * @param {Element} nacl DOM Element embedding NaCl module.
 */
skk.IME = function(nacl) {
  this.engineID = null;
  this.isActive_ = false;
  this.modeStack_ = [new skk.mode.Kana(this)];

  this.context = null;
  this.dict = new skk.Dict(nacl);
  this.registerEventHandlers();
};

/**
 * Returns enabled ime mode.
 * @return {skk.mode.InputModeInterface} Input mode object.
 */
skk.IME.prototype.getCurrentMode = function() {
  return this.modeStack_[this.modeStack_.length - 1];
};

/**
 * Switches current ime mode.
 * @param {skk.InputMode} newMode Input mode to switch.
 * @param {function(skk.InputModeInterface)=} opt_callback
 *     Callback function that takes new input mode object as an argument.
 *     This is called back before {@code enter} method is invoked.
 */
skk.IME.prototype.switchMode = function(newMode, opt_callback) {
  console.log('skk.IME.switchMode: ', newMode);
  this.leaveCurrentMode();
  this.enterNewMode(newMode, opt_callback);
};

/**
 * Suspends current ime mode, then initializes a new ime mode and enables it.
 * @param {skk.InputMode} newMode Class name to be newly initialized.
 * @param {function(skk.InputModeInterface)=} opt_callback
 *     Callback function that takes new input mode object as an argument.
 *     This is called back before {@code enter} method is invoked.
 */
skk.IME.prototype.enterNewMode = function(newMode, opt_callback) {
  console.log('skk.IME.enterNewMode: ', newMode);
  if (this.modeStack_.length !== 0) { this.getCurrentMode().suspend(); }
  var mode = new skk.mode[newMode](this);
  this.modeStack_.push(mode);
  if (!!opt_callback) { opt_callback(mode); }
  mode.enter();
};

/**
 * Disables current ime mode and resumes recently suspended mode.
 */
skk.IME.prototype.leaveCurrentMode = function() {
  this.modeStack_.pop().leave();
  if (this.modeStack_.length !== 0) { this.getCurrentMode().resume(); }
};

/**
 * Registers event handlers to the browser.
 */
skk.IME.prototype.registerEventHandlers = function() {
  var self = this;

  chrome.experimental.input.onActivate.addListener(function(engineID) {
    console.log('ime.js: onActivate');
    self.isActive_ = true;
    self.engineID = engineID;
    chrome.experimental.input.setCandidateWindowProperties(
        {
          engineID: self.engineID,
          properties: {
            visible: false,
            cursorVisible: false,
            pageSize: skk.NUM_CANDIDATES
          }
        });
  });

  chrome.experimental.input.onDeactivated.addListener(function() {
    console.log('ime.js: onDeactivated');
    self.isActive_ = false;
    self.context = null;
    self.getCurrentMode().reset();
  });

  chrome.experimental.input.onFocus.addListener(function(context) {
    console.log('ime.js: onFocus');
    console.debug('context id: ', context.contextID);
    self.context = context;
  });

  chrome.experimental.input.onBlur.addListener(function(contextID) {
    console.log('ime.js: onBlur');
    if (!self.context) { return; }
    if (self.context.contextID !== contextID) { return; }
    self.getCurrentMode().reset();
    self.context = null;
  });

  chrome.experimental.input.onKeyEvent.addListener(function(engine, keyEvent) {
    console.log('ime.js: onKeyEvent: ', keyEvent);
    if (keyEvent.type == 'keyup') { return true; }

    keyEvent = self.getCurrentMode().prepareForKey(keyEvent);
    if (!keyEvent) { return true; }

    var handled = self.getCurrentMode().addKey(keyEvent);
    console.debug('ime.js: onKeyEvent: handled = ', handled);
    return handled;
  });

  chrome.experimental.input.onInputContextUpdate.addListener(function() {
    console.log('ime.js: onInputContextUpdate');
  });

  chrome.experimental.input.onCandidateClicked.addListener(
      function(engineID, candidateID, button) {
        console.log('ime.js: onCandidateClicked');
        if (self.engineID !== engineID) { return; }
        console.assert(this.ime_.getCurrentMode() instanceof
                       skk.mode.Conversion,
                       'ime.js: onCandidateClicked: Internal error: ' +
                           'Current input mode must be skk.mode.Conversion');
        self.getCurrentMode().commitCandidate(candidateID);
      });

  chrome.experimental.input.onMenuItemActivated.addListener(function() {
    console.log('ime.js: onMenuItemActivated');
  });
};

// Since IME constructor requires NaCl module,
// initialization must be deferred until the module's DOM Element is created.
document.addEventListener(
    'readystatechange',
    function() {
      if (document.readyState === 'complete') {
        var naclModule = document.getElementById('skk_dict');
        new skk.IME(naclModule);
      }
    });