Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
243 lines
6.9 KiB
JavaScript
Executable file
243 lines
6.9 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
(function(exports) {
|
|
|
|
var LayoutItemErrorInfo = function(layoutItemState) {
|
|
switch (layoutItemState) {
|
|
case LayoutItem.prototype.STATE_INSTALLING_CANCELLABLE:
|
|
this.error = this.ERROR_DOWNLOADERROR;
|
|
break;
|
|
|
|
case LayoutItem.prototype.STATE_INSTALLING:
|
|
this.error = this.ERROR_INSTALLERROR;
|
|
break;
|
|
|
|
case LayoutItem.prototype.STATE_REMOVING:
|
|
this.error = this.ERROR_REMOVEERROR;
|
|
break;
|
|
|
|
default:
|
|
throw new Error('LayoutItemErrorInfo: Error at invalid state.');
|
|
}
|
|
};
|
|
LayoutItemErrorInfo.prototype.ERROR_DOWNLOADERROR = 1;
|
|
LayoutItemErrorInfo.prototype.ERROR_INSTALLERROR = 2;
|
|
LayoutItemErrorInfo.prototype.ERROR_REMOVEERROR = 3;
|
|
|
|
var LayoutItem = function(list, layout) {
|
|
this.list = list;
|
|
|
|
this.id = layout.id;
|
|
this.name = layout.name;
|
|
this.nameL10nId = layout.nameL10nId;
|
|
this.imEngineId = layout.imEngineId;
|
|
this.dictFilePath = layout.dictFilePath;
|
|
this.fileSize = layout.dictFileSize;
|
|
|
|
this.types = layout.types;
|
|
|
|
// Set the initial state.
|
|
if (layout.preloaded) {
|
|
this.state = this.STATE_PRELOADED;
|
|
} else if (layout.installed) {
|
|
this.state = this.STATE_INSTALLED;
|
|
} else {
|
|
this.state = this.STATE_INSTALLABLE;
|
|
}
|
|
|
|
this._dictionary = null;
|
|
this._userCancelled = undefined;
|
|
};
|
|
|
|
LayoutItem.prototype.STATE_PRELOADED = 1;
|
|
LayoutItem.prototype.STATE_INSTALLABLE = 2;
|
|
LayoutItem.prototype.STATE_INSTALLING_CANCELLABLE = 3;
|
|
LayoutItem.prototype.STATE_INSTALLING = 4;
|
|
LayoutItem.prototype.STATE_INSTALLED = 5;
|
|
LayoutItem.prototype.STATE_REMOVING = 6;
|
|
|
|
LayoutItem.prototype.onerror = null;
|
|
LayoutItem.prototype.onstatechange = null;
|
|
LayoutItem.prototype.onprogress = null;
|
|
LayoutItem.prototype.oninstall = null;
|
|
|
|
LayoutItem.prototype.id = '';
|
|
LayoutItem.prototype.name = '';
|
|
LayoutItem.prototype.nameL10nId = '';
|
|
LayoutItem.prototype.types = null;
|
|
LayoutItem.prototype.fileSize = 0;
|
|
LayoutItem.prototype.downloadLoadedSize = 0;
|
|
LayoutItem.prototype.downloadTotalSize = 0;
|
|
|
|
LayoutItem.prototype.start = function() {
|
|
if (this.state !== this.STATE_PRELOADED) {
|
|
this._dictionary = this.list.dictionaryList
|
|
.getDictionary(this.imEngineId, this.dictFilePath);
|
|
}
|
|
};
|
|
|
|
LayoutItem.prototype.stop = function() {
|
|
// noop
|
|
};
|
|
|
|
LayoutItem.prototype.install = function() {
|
|
if (this.state !== this.STATE_INSTALLABLE) {
|
|
throw new Error('LayoutItem: ' +
|
|
'Can\'t install a layout under this state.');
|
|
}
|
|
|
|
var openLock = this.list.closeLockManager.requestLock('stayAwake');
|
|
|
|
this._userCancelled = false;
|
|
|
|
var p = Promise.resolve()
|
|
.then(function() {
|
|
var p = this._dictionary.installForLayout(this);
|
|
|
|
if (this._dictionary.state ===
|
|
this._dictionary.STATE_INSTALLING_CANCELLABLE) {
|
|
this.downloadLoadedSize = this._dictionary.downloadLoadedSize;
|
|
this.downloadTotalSize = this._dictionary.downloadTotalSize;
|
|
this._changeState(this.STATE_INSTALLING_CANCELLABLE);
|
|
} else {
|
|
this._changeState(this.STATE_INSTALLING);
|
|
}
|
|
|
|
// Resolve until the dictionary is in the installed state.
|
|
return p;
|
|
}.bind(this))
|
|
.then(function() {
|
|
if (this.state !== this.STATE_INSTALLING) {
|
|
this.downloadLoadedSize = 0;
|
|
this.downloadTotalSize = 0;
|
|
|
|
this._changeState(this.STATE_INSTALLING);
|
|
}
|
|
|
|
return navigator.mozInputMethod.addInput(this.id, {
|
|
launch_path: '/index.html#' + this.id,
|
|
name: this.name,
|
|
description: this.name,
|
|
types: this.types
|
|
});
|
|
}.bind(this))
|
|
.then(this.list.setLayoutAsInstalled.bind(this.list, this.id))
|
|
.then(function() {
|
|
this._userCancelled = undefined;
|
|
openLock.unlock();
|
|
this._changeState(this.STATE_INSTALLED);
|
|
|
|
if (typeof this.oninstall === 'function') {
|
|
this.oninstall();
|
|
}
|
|
}.bind(this))
|
|
.catch(function(e) {
|
|
if (!this._userCancelled) {
|
|
this._triggerError();
|
|
}
|
|
|
|
this._userCancelled = undefined;
|
|
this.downloadLoadedSize = 0;
|
|
this.downloadTotalSize = 0;
|
|
|
|
openLock.unlock();
|
|
this._changeState(this.STATE_INSTALLABLE);
|
|
|
|
throw (e || new Error('LayoutItem: Installation failed/cancelled.'));
|
|
}.bind(this));
|
|
|
|
return p;
|
|
};
|
|
|
|
LayoutItem.prototype.cancelInstall = function() {
|
|
if (this.state !== this.STATE_INSTALLING_CANCELLABLE) {
|
|
throw new Error('LayoutItem: ' +
|
|
'Can\'t cancel an install under this state.');
|
|
}
|
|
|
|
this._userCancelled = true;
|
|
this._dictionary.removeForLayout(this);
|
|
};
|
|
|
|
LayoutItem.prototype.remove = function() {
|
|
if (this.state !== this.STATE_INSTALLED) {
|
|
throw new Error('LayoutItem: ' +
|
|
'Can\'t remove a layout under this state.');
|
|
}
|
|
|
|
var openLock = this.list.closeLockManager.requestLock('stayAwake');
|
|
|
|
var p = Promise.resolve()
|
|
.then(function() {
|
|
this._changeState(this.STATE_REMOVING);
|
|
return navigator.mozInputMethod.removeInput(this.id);
|
|
}.bind(this))
|
|
.then(function() {
|
|
return this.list.setLayoutAsUninstalled(this.id)
|
|
.catch(function(e) {
|
|
// This is not good; we'd de-registered globally but failed to
|
|
// de-register internally ourselves.
|
|
// There isn't an obvious way to fix this, other than ignoring the
|
|
// error and hope the user can fix the inconsistency by complete
|
|
// another install-remove cycle.
|
|
console.error(
|
|
'LayoutItem: Ignoring set uninstall failure.', e);
|
|
});
|
|
}.bind(this))
|
|
.then(function() {
|
|
return this._dictionary.removeForLayout(this)
|
|
.catch(function(e) {
|
|
// Ignore failed removal and still mark the layout as removed here,
|
|
// since we have already de-registered at this point.
|
|
console.error(
|
|
'LayoutItem: Ignoring dictionary removal failure.', e);
|
|
});
|
|
}.bind(this))
|
|
.then(openLock.unlock.bind(openLock))
|
|
.then(this._changeState.bind(this, this.STATE_INSTALLABLE))
|
|
.catch(function(e) {
|
|
this._triggerError();
|
|
openLock.unlock();
|
|
this._changeState(this.STATE_INSTALLED);
|
|
|
|
throw (e || new Error('LayoutItem: Removing failed.'));
|
|
}.bind(this));
|
|
|
|
return p;
|
|
};
|
|
|
|
// Will be called by LayoutDictionary during the downloading process.
|
|
LayoutItem.prototype.updateInstallProgress = function(loaded, total) {
|
|
if (this.state !== this.STATE_INSTALLING_CANCELLABLE) {
|
|
console.warn(
|
|
'LayoutItem: Ignoring updateInstallProgress call in the wrong state.');
|
|
|
|
return;
|
|
}
|
|
|
|
this.downloadLoadedSize = loaded;
|
|
this.downloadTotalSize = total;
|
|
|
|
if (typeof this.onprogress === 'function') {
|
|
return this.onprogress(loaded, total);
|
|
}
|
|
};
|
|
|
|
LayoutItem.prototype._triggerError = function() {
|
|
if (typeof this.onerror === 'function') {
|
|
this.onerror(new LayoutItemErrorInfo(this.state));
|
|
}
|
|
};
|
|
|
|
LayoutItem.prototype._changeState = function(state) {
|
|
this.state = state;
|
|
|
|
if (typeof this.onstatechange === 'function') {
|
|
return this.onstatechange();
|
|
}
|
|
};
|
|
|
|
exports.LayoutItemErrorInfo = LayoutItemErrorInfo;
|
|
exports.LayoutItem = LayoutItem;
|
|
|
|
}(window));
|