Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,83 @@
Handwriting is a handwriting recoginiton input method for gaia and is licensed
under the Apache Version 2.0, 2014. The handwriting recoginition engine is
zinnia (http://zinnia.sourceforge.net/), which is converted to JavaScript as
emscripten-zinnia project and hosted at:
https://github.com/yxl/emscripten-zinnia.
Emscripten (https://github.com/kripken/emscripten) is used to compile the
original C++ zinnia code into JavaScript version.
==========================================================================
Copyright notice for handwriting.
Copyright (c) 2014, Mozilla Online
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==========================================================================
Copyright notice for emscripten-zinnia.
Copyright (c) 2014, Yuan Xulei
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 Taku Kudo 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 HOLDER 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.
==========================================================================
Copyright notice for zinnia.
Copyright (c) 2005-2007, Taku Kudo
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 Taku Kudo 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 HOLDER 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.

View file

@ -0,0 +1,288 @@
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
/* global InputMethods, KeyEvent */
/*
* This file implements a "glue" between FxOS Keyboard app UI and
* the handwriting recognition library. The recognition engine works
* in a web worker, it could support multiple languages, and communicates
* with this "glue" with messages.
*/
var HandwritingGaiaKeyboardGlue = function HandwritingGaiaKeyboardGlue() {
this.callbacks = null;
this.worker = null;
// The state of handwriting recognition engine module,
// whether it has been loaded into web worker completely.
this.moduleLoaded = false;
// The state of handwriting recognition engine,
// whether it's ready for for recognition.
this.isReady = false;
this.firstCandidate = '';
this.keypressQueue = [];
this.isWorking = false;
this.isActive = false;
// Current language
this.language = '';
this.isPredicting = false;
// For prediction
this.words = '';
this.cachedPoints = [];
this.unloadWorkerTimer = undefined;
};
HandwritingGaiaKeyboardGlue.prototype.DEACTIVATE_TIME = 60000; // 1 minute
HandwritingGaiaKeyboardGlue.prototype.init = function hr_init(callbacks) {
this.callbacks = callbacks;
};
HandwritingGaiaKeyboardGlue.prototype.uninit = function hr_uninit(callbacks) {
this._unloadWorker();
this.callbacks = null;
};
HandwritingGaiaKeyboardGlue.prototype._loadWorker = function hr_loadWorker() {
if (this.worker) {
return;
}
this.worker = new Worker(this.callbacks.path + '/hwr/worker.js');
this.worker.onmessage = function(evt) {
var data = evt.data;
switch (data.id) {
case 'init':
this.moduleLoaded = true;
this.postMessage('setLang', this.language);
break;
case 'setLang':
this.isReady = data.value;
if (this.isReady) {
this._handleCachedStrokePoints();
}
break;
case 'recognize':
this.isPredicting = false;
this._sendCandidates(JSON.parse(data.value));
break;
case 'getPrediction':
this.isPredicting = true;
var predictions = data.value;
if (predictions) {
this._sendCandidates(predictions.split(' '));
}
break;
default:
break;
}
}.bind(this);
};
HandwritingGaiaKeyboardGlue.prototype._unloadWorker =
function hr_unloadWorker() {
if (this.worker) {
this.worker.terminate();
}
this.handlers = null;
this.worker = null;
this.moduleLoaded = false;
this.isReady = false;
};
HandwritingGaiaKeyboardGlue.prototype.select = function hr_select(text, data) {
this.callbacks.sendString(text);
this.empty();
this._start();
if (this.isPredicting) {
this.words += text;
} else {
this.words = text;
this.isPredicting = true;
}
this.postMessage('getPrediction', this.words);
};
HandwritingGaiaKeyboardGlue.prototype.click = function hr_click(keyCode) {
this.keypressQueue.push(keyCode);
this._start();
};
HandwritingGaiaKeyboardGlue.prototype.activate =
function hr_activate(language, state, options) {
var needsSetLang = this.language != language;
this.language = language;
if (!this.worker) {
this._loadWorker();
}
clearTimeout(this.unloadWorkerTimer);
// The handwriting recognition engine may be used by multiple IMEs,
// we need set language for different IMEs.
if (this.moduleLoaded && needsSetLang) {
this.isReady = false;
this.postMessage('setLang', language);
}
this.isActive = true;
};
HandwritingGaiaKeyboardGlue.prototype.deactivate = function hr_deactivate() {
if (!this._isActive) {
return;
}
this.isActive = false;
this._resetKeypressQueue();
this.empty();
clearTimeout(this.unloadWorkerTimer);
this.unloadWorkerTimer =
setTimeout(this._unloadWorker.bind(this), this.DEACTIVATE_TIME);
};
HandwritingGaiaKeyboardGlue.prototype.empty = function hr_empty() {
this.firstCandidate = '';
this.callbacks.sendCandidates([]);
this.callbacks.endComposition();
this.callbacks.app.handwritingPadsManager.clear();
};
HandwritingGaiaKeyboardGlue.prototype.postMessage = function(id, param) {
if (!this.moduleLoaded) {
return;
}
this.worker.postMessage({
id: id,
param: param
});
};
/**
* Send candidates list and change composition.
* @param {Array.<string>} candidates The candidates to be sent.
* @return {void} No return value.
*/
HandwritingGaiaKeyboardGlue.prototype._sendCandidates =
function hr_sendCandidates(candidates) {
var list = [];
var len = candidates.length;
for (var id = 0; id < len; id++) {
var cand = candidates[id];
list.push([cand, id]);
}
if (len > 0) {
this.firstCandidate = candidates[0];
}
this.callbacks.sendCandidates(list);
if (this.isPredicting) {
return;
}
if (this.firstCandidate) {
this.callbacks.setComposition(this.firstCandidate);
}
};
HandwritingGaiaKeyboardGlue.prototype._start = function hr_start() {
if (this.isWorking) {
return;
}
this.isWorking = true;
this._next();
};
HandwritingGaiaKeyboardGlue.prototype._next = function hr_next() {
if (!this.keypressQueue.length) {
this.isWorking = false;
return;
}
var code = this.keypressQueue.shift();
var sendKey = true;
if (this.firstCandidate) {
switch (code) {
case KeyEvent.DOM_VK_BACK_SPACE:
sendKey = false;
this.empty();
break;
case KeyEvent.DOM_VK_RETURN:
case KeyEvent.DOM_VK_SPACE:
// candidate list exists; output the first candidate
this.select(this.firstCandidate);
this.callbacks.app.handwritingPadsManager.clear();
sendKey = false;
break;
default:
this.callbacks.endComposition(this.firstCandidate);
this.empty();
break;
}
}
//pass the key to IMEManager for default action
if (sendKey) {
this.callbacks.sendKey(code);
}
this._next();
};
HandwritingGaiaKeyboardGlue.prototype._resetKeypressQueue =
function hr_resetKeypressQueue() {
this.keypressQueue = [];
this.isWorking = false;
};
// If handwriting recognition engine is loaded or initialized later
// than user writing, there are some cached stroke points, do
// recognition for cached stroke points.
HandwritingGaiaKeyboardGlue.prototype._handleCachedStrokePoints =
function hr_handleCachedStrokePoints() {
if (this.cachedPoints.length === 0) {
return;
}
this.postMessage('recognize', JSON.stringify(this.cachedPoints));
this.cachedPoints = [];
};
HandwritingGaiaKeyboardGlue.prototype.sendStrokePoints =
function hr_sendStrokePoints(strokePoints) {
// If handwriting recognition engine is not loaded or is not ready for
// recognition, cache the stroke points.
if (!this.moduleLoaded || !this.isReady) {
this.cachedPoints = strokePoints;
return;
}
this.postMessage('recognize', JSON.stringify(strokePoints));
};
// Expose the engine to the Gaia keyboard
if (typeof InputMethods !== 'undefined') {
InputMethods.handwriting = new HandwritingGaiaKeyboardGlue();
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,122 @@
'use strict';
var MAX_STROKE_NUM = 1000;
self.onmessage = function(e) {
var data = e.data;
switch(data.id) {
case 'setLang':
var ret = Recognition.setLang(data.param);
post('setLang', ret);
break;
case 'recognize':
var words = Recognition.recognize(JSON.parse(data.param));
post('recognize', JSON.stringify(words));
break;
case 'getPrediction':
var words = Recognition.getPrediction(data.param);
post('getPrediction', words);
break;
default:
break;
}
};
var Recognition = {
_inited: false,
setLang: function(lang) {
if (!this._lang) {
this._lang = lang;
return this._init(lang);
}
if (this._lang != lang) {
this._lang = lang;
this._uinit();
return this._init(lang);
}
return true;
},
recognize: function(points) {
if (!this._inited) {
return [];
}
var strokes = points.slice(0, 2 * MAX_STROKE_NUM);
// Append [-1, -1] to strokes.
strokes.push.apply(strokes, [-1, -1]);
this._strokeBuffer.set(strokes);
return this._CharacterRecognize(this._strokeBuffer.byteOffset);
},
getPrediction: function(words) {
if (this._getPrediction) {
return this._getPrediction(words);
}
},
_init: function(lang) {
this._inited = false;
var model = '';
if (lang == 'zh-Hans') {
model = 'zh_Hans.model';
}
if (lang == 'zh-Hant') {
model = 'zh_Hant.model';
}
if (!Module.ccall('InitRecog',
'number',
['string', 'number', 'number'],
[model, 140, 140])) {
return false;
}
this._CharacterRecognize =
Module.cwrap('CharacterRecognize', 'string', ['number']);
if (Module.ccall('InitPrediction',
'number',
['string'],
['dict_pinyin.data'])) {
this._getPrediction =
Module.cwrap('GetPrediction', 'string', ['string']);
}
var buf = Module._malloc((MAX_STROKE_NUM + 1) * 4);
this._strokeBuffer =
new Int16Array(Module.HEAPU8.buffer, buf, (MAX_STROKE_NUM + 1) * 2);
this._inited = true;
return true;
},
_uinit: function() {
Module._free(this._strokeBuffer.byteOffset);
Module.ccall('ExitRecog', '', [], []);
},
_strokeBuffer: null,
_CharacterRecognize: null,
_lang: '',
_getPrediction: null
};
var Module = {
filePackagePrefixURL: './',
canvas: {},
stdout: null,
_main: function() {
post('init', true);
}
};
function post(id, value) {
postMessage({
id: id,
value: value
});
}

View file

@ -0,0 +1,7 @@
'use strict';
/**
* Load Recognition, etc. into a Web Worker.
*/
importScripts('recognition.js', 'emscripten_zinnia.js');