mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-18 14:27:34 +09:00
280 lines
6.9 KiB
JavaScript
280 lines
6.9 KiB
JavaScript
/*
|
|
* Copyright 2012, Mozilla Foundation and contributors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var util = require('../util/util');
|
|
var host = require('../util/host');
|
|
|
|
// It's probably easiest to read this bottom to top
|
|
|
|
/**
|
|
* Best guess at creating a DOM element from random data
|
|
*/
|
|
var fallbackDomConverter = {
|
|
from: '*',
|
|
to: 'dom',
|
|
exec: function(data, conversionContext) {
|
|
return conversionContext.document.createTextNode(data || '');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Best guess at creating a string from random data
|
|
*/
|
|
var fallbackStringConverter = {
|
|
from: '*',
|
|
to: 'string',
|
|
exec: function(data, conversionContext) {
|
|
return data == null ? '' : data.toString();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Convert a view object to a DOM element
|
|
*/
|
|
var viewDomConverter = {
|
|
item: 'converter',
|
|
from: 'view',
|
|
to: 'dom',
|
|
exec: function(view, conversionContext) {
|
|
if (!view.isView) {
|
|
view = conversionContext.createView(view);
|
|
}
|
|
return view.toDom(conversionContext.document);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Convert a view object to a string
|
|
*/
|
|
var viewStringConverter = {
|
|
item: 'converter',
|
|
from: 'view',
|
|
to: 'string',
|
|
exec: function(view, conversionContext) {
|
|
if (!view.isView) {
|
|
view = conversionContext.createView(view);
|
|
}
|
|
return view.toDom(conversionContext.document).textContent;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Convert a view object to a string
|
|
*/
|
|
var stringViewStringConverter = {
|
|
item: 'converter',
|
|
from: 'stringView',
|
|
to: 'string',
|
|
exec: function(view, conversionContext) {
|
|
if (!view.isView) {
|
|
view = conversionContext.createView(view);
|
|
}
|
|
return view.toDom(conversionContext.document).textContent;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Convert an exception to a DOM element
|
|
*/
|
|
var errorDomConverter = {
|
|
item: 'converter',
|
|
from: 'error',
|
|
to: 'dom',
|
|
exec: function(ex, conversionContext) {
|
|
var node = util.createElement(conversionContext.document, 'p');
|
|
node.className = 'gcli-error';
|
|
node.textContent = errorStringConverter.exec(ex, conversionContext);
|
|
return node;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Convert an exception to a string
|
|
*/
|
|
var errorStringConverter = {
|
|
item: 'converter',
|
|
from: 'error',
|
|
to: 'string',
|
|
exec: function(ex, conversionContext) {
|
|
if (typeof ex === 'string') {
|
|
return ex;
|
|
}
|
|
if (ex instanceof Error) {
|
|
return '' + ex;
|
|
}
|
|
if (typeof ex.message === 'string') {
|
|
return ex.message;
|
|
}
|
|
return '' + ex;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a new converter by using 2 converters, one after the other
|
|
*/
|
|
function getChainConverter(first, second) {
|
|
if (first.to !== second.from) {
|
|
throw new Error('Chain convert impossible: ' + first.to + '!=' + second.from);
|
|
}
|
|
return {
|
|
from: first.from,
|
|
to: second.to,
|
|
exec: function(data, conversionContext) {
|
|
var intermediate = first.exec(data, conversionContext);
|
|
return second.exec(intermediate, conversionContext);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* A manager for the registered Converters
|
|
*/
|
|
function Converters() {
|
|
// This is where we cache the converters that we know about
|
|
this._registered = {
|
|
from: {}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Add a new converter to the cache
|
|
*/
|
|
Converters.prototype.add = function(converter) {
|
|
var fromMatch = this._registered.from[converter.from];
|
|
if (fromMatch == null) {
|
|
fromMatch = {};
|
|
this._registered.from[converter.from] = fromMatch;
|
|
}
|
|
|
|
fromMatch[converter.to] = converter;
|
|
};
|
|
|
|
/**
|
|
* Remove an existing converter from the cache
|
|
*/
|
|
Converters.prototype.remove = function(converter) {
|
|
var fromMatch = this._registered.from[converter.from];
|
|
if (fromMatch == null) {
|
|
return;
|
|
}
|
|
|
|
if (fromMatch[converter.to] === converter) {
|
|
fromMatch[converter.to] = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Work out the best converter that we've got, for a given conversion.
|
|
*/
|
|
Converters.prototype.get = function(from, to) {
|
|
var fromMatch = this._registered.from[from];
|
|
if (fromMatch == null) {
|
|
return this._getFallbackConverter(from, to);
|
|
}
|
|
|
|
var converter = fromMatch[to];
|
|
if (converter == null) {
|
|
// Someone is going to love writing a graph search algorithm to work out
|
|
// the smallest number of conversions, or perhaps the least 'lossy'
|
|
// conversion but for now the only 2 step conversions which we are going to
|
|
// special case are foo->view->dom and foo->stringView->string.
|
|
if (to === 'dom') {
|
|
converter = fromMatch.view;
|
|
if (converter != null) {
|
|
return getChainConverter(converter, viewDomConverter);
|
|
}
|
|
}
|
|
|
|
if (to === 'string') {
|
|
converter = fromMatch.stringView;
|
|
if (converter != null) {
|
|
return getChainConverter(converter, stringViewStringConverter);
|
|
}
|
|
converter = fromMatch.view;
|
|
if (converter != null) {
|
|
return getChainConverter(converter, viewStringConverter);
|
|
}
|
|
}
|
|
|
|
return this._getFallbackConverter(from, to);
|
|
}
|
|
return converter;
|
|
};
|
|
|
|
/**
|
|
* Get all the registered converters. Most for debugging
|
|
*/
|
|
Converters.prototype.getAll = function() {
|
|
return Object.keys(this._registered.from).map(function(name) {
|
|
return this._registered.from[name];
|
|
}.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Helper for get to pick the best fallback converter
|
|
*/
|
|
Converters.prototype._getFallbackConverter = function(from, to) {
|
|
console.error('No converter from ' + from + ' to ' + to + '. Using fallback');
|
|
|
|
if (to === 'dom') {
|
|
return fallbackDomConverter;
|
|
}
|
|
|
|
if (to === 'string') {
|
|
return fallbackStringConverter;
|
|
}
|
|
|
|
throw new Error('No conversion possible from ' + from + ' to ' + to + '.');
|
|
};
|
|
|
|
/**
|
|
* Convert some data from one type to another
|
|
* @param data The object to convert
|
|
* @param from The type of the data right now
|
|
* @param to The type that we would like the data in
|
|
* @param conversionContext An execution context (i.e. simplified requisition)
|
|
* which is often required for access to a document, or createView function
|
|
*/
|
|
Converters.prototype.convert = function(data, from, to, conversionContext) {
|
|
try {
|
|
if (from === to) {
|
|
return Promise.resolve(data);
|
|
}
|
|
|
|
var converter = this.get(from, to);
|
|
return host.exec(function() {
|
|
return converter.exec(data, conversionContext);
|
|
}.bind(this));
|
|
}
|
|
catch (ex) {
|
|
var converter = this.get('error', to);
|
|
return host.exec(function() {
|
|
return converter.exec(ex, conversionContext);
|
|
}.bind(this));
|
|
}
|
|
};
|
|
|
|
exports.Converters = Converters;
|
|
|
|
/**
|
|
* Items for export
|
|
*/
|
|
exports.items = [
|
|
viewDomConverter, viewStringConverter, stringViewStringConverter,
|
|
errorDomConverter, errorStringConverter
|
|
];
|