Magic operation now displays an ordered table of the most likely decodings.

This commit is contained in:
n1474335 2018-01-22 23:34:24 +00:00
parent 28abd00d82
commit 6947d2a7f3
5 changed files with 266 additions and 31 deletions

View File

@ -88,7 +88,7 @@ self.addEventListener("message", function(e) {
*/
async function bake(data) {
// Ensure the relevant modules are loaded
loadRequiredModules(data.recipeConfig);
self.loadRequiredModules(data.recipeConfig);
try {
const response = await self.chef.bake(
@ -125,24 +125,6 @@ function silentBake(data) {
}
/**
* Checks that all required modules are loaded and loads them if not.
*
* @param {Object} recipeConfig
*/
function loadRequiredModules(recipeConfig) {
recipeConfig.forEach(op => {
let module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
log.info("Loading module " + module);
self.sendStatusMessage("Loading module " + module);
self.importScripts(self.docURL + "/" + module + ".js");
}
});
}
/**
* Calculates highlight offsets if possible.
*
@ -162,6 +144,24 @@ function calculateHighlights(recipeConfig, direction, pos) {
}
/**
* Checks that all required modules are loaded and loads them if not.
*
* @param {Object} recipeConfig
*/
self.loadRequiredModules = function(recipeConfig) {
recipeConfig.forEach(op => {
let module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
log.info("Loading module " + module);
self.sendStatusMessage("Loading module " + module);
self.importScripts(self.docURL + "/" + module + ".js");
}
});
};
/**
* Send status update to the app.
*

View File

@ -1,6 +1,7 @@
import Recipe from "./Recipe.js";
import Dish from "./Dish.js";
import Magic from "./lib/Magic.js";
import Utils from "./Utils.js";
/**
@ -267,12 +268,47 @@ const FlowControl = {
const ings = state.opList[state.progress].getIngValues(),
depth = ings[0],
dish = state.dish,
magic = new Magic(dish.get(Dish.ARRAY_BUFFER));
currentRecipeConfig = state.opList.map(op => op.getConfig()),
magic = new Magic(dish.get(Dish.ARRAY_BUFFER)),
options = await magic.speculativeExecution(depth);
const specExec = await magic.speculativeExecution(depth+1);
const output = JSON.stringify(specExec, null, 2);
let output = `<table
class='table table-hover table-condensed table-bordered'
style='table-layout: fixed;'>
<tr>
<th>Recipe (click to load)</th>
<th>Data snippet</th>
<th>Most likely language\n(lower scores are better)</th>
<th>File type</th>
</tr>`;
dish.set(output, Dish.STRING);
options.forEach(option => {
// Construct recipe URL
// Replace this Magic op with the generated recipe
const recipeConfig = currentRecipeConfig.slice(0, state.progress)
.concat(option.recipe)
.concat(currentRecipeConfig.slice(state.progress + 1)),
recipeURL = "recipe=" + Utils.encodeURIFragment(Utils.generatePrettyRecipe(recipeConfig));
const language = option.languageScores[0];
let fileType = "Unknown";
if (option.fileType) {
fileType = `Extension: ${option.fileType.ext}\nMime type: ${option.fileType.mime}`;
if (option.fileType.desc)
fileType += `\nDescription: ${option.fileType.desc}`;
}
output += `<tr>
<td><a href="#${recipeURL}">${Utils.generatePrettyRecipe(option.recipe, true)}</a></td>
<td>${Utils.escapeHtml(Utils.printable(option.data))}</td>
<td>${Magic.codeToLanguage(language.lang)}\nScore: ${language.chiSqr.toFixed()}</td>
<td>${fileType}</td>
</tr>`;
});
output += "</table>";
dish.set(output, Dish.HTML);
return state;
},

View File

@ -908,10 +908,10 @@ const Utils = {
* for users.
*
* @param {Object[]} recipeConfig
* @param {boolean} newline - whether to add a newline after each operation
* @param {boolean} [newline=false] - whether to add a newline after each operation
* @returns {string}
*/
generatePrettyRecipe: function(recipeConfig, newline) {
generatePrettyRecipe: function(recipeConfig, newline = false) {
let prettyConfig = "",
name = "",
args = "",

View File

@ -85,7 +85,7 @@ const OperationConfig = {
module: "Default",
description: "Attempts to detect what the input data is and which operations could help to make more sense of it.",
inputType: "ArrayBuffer",
outputType: "string",
outputType: "html",
flowControl: true,
args: [
{

View File

@ -96,12 +96,12 @@ class Magic {
/**
* Speculatively executes matching operations, recording metadata of each result.
*
* @param {number} [depth=1] - How many levels to try to execute
* @param {number} [depth=0] - How many levels to try to execute
* @param {Object[]} [recipeConfig=[]] - The recipe configuration up to this point
* @returns {Object[]} A sorted list of the recipes most likely to result in correct decoding
*/
async speculativeExecution(depth = 1, recipeConfig = []) {
if (depth === 0) return [];
async speculativeExecution(depth = 0, recipeConfig = []) {
if (depth < 0) return [];
let results = [];
@ -123,12 +123,16 @@ class Magic {
opConfig = {
op: op.op,
args: op.args
},
recipe = new Recipe([opConfig]);
};
if (ENVIRONMENT_IS_WORKER()) self.loadRequiredModules([opConfig]);
const recipe = new Recipe([opConfig]);
await recipe.execute(dish, 0);
const magic = new Magic(dish.get(Dish.ARRAY_BUFFER)),
speculativeResults = await magic.speculativeExecution(depth-1, [...recipeConfig, opConfig]);
results = results.concat(speculativeResults);
}));
@ -215,6 +219,201 @@ class Magic {
return res;
}
/**
* Translates an ISO 639-1 code to a full language name.
*
* @param {string} code - The two letter ISO 639-1 code
* @returns {string} The full name of the languge
*/
static codeToLanguage(code) {
return {
"aa": "Afar",
"ab": "Abkhazian",
"ae": "Avestan",
"af": "Afrikaans",
"ak": "Akan",
"am": "Amharic",
"an": "Aragonese",
"ar": "Arabic",
"as": "Assamese",
"av": "Avaric",
"ay": "Aymara",
"az": "Azerbaijani",
"ba": "Bashkir",
"be": "Belarusian",
"bg": "Bulgarian",
"bh": "Bihari languages",
"bi": "Bislama",
"bm": "Bambara",
"bn": "Bengali",
"bo": "Tibetan",
"br": "Breton",
"bs": "Bosnian",
"ca": "Catalan; Valencian",
"ce": "Chechen",
"ch": "Chamorro",
"co": "Corsican",
"cr": "Cree",
"cs": "Czech",
"cu": "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic",
"cv": "Chuvash",
"cy": "Welsh",
"da": "Danish",
"de": "German",
"dv": "Divehi; Dhivehi; Maldivian",
"dz": "Dzongkha",
"ee": "Ewe",
"el": "Greek, Modern (1453-)",
"en": "English",
"eo": "Esperanto",
"es": "Spanish; Castilian",
"et": "Estonian",
"eu": "Basque",
"fa": "Persian",
"ff": "Fulah",
"fi": "Finnish",
"fj": "Fijian",
"fo": "Faroese",
"fr": "French",
"fy": "Western Frisian",
"ga": "Irish",
"gd": "Gaelic; Scottish Gaelic",
"gl": "Galician",
"gn": "Guarani",
"gu": "Gujarati",
"gv": "Manx",
"ha": "Hausa",
"he": "Hebrew",
"hi": "Hindi",
"ho": "Hiri Motu",
"hr": "Croatian",
"ht": "Haitian; Haitian Creole",
"hu": "Hungarian",
"hy": "Armenian",
"hz": "Herero",
"ia": "Interlingua (International Auxiliary Language Association)",
"id": "Indonesian",
"ie": "Interlingue; Occidental",
"ig": "Igbo",
"ii": "Sichuan Yi; Nuosu",
"ik": "Inupiaq",
"io": "Ido",
"is": "Icelandic",
"it": "Italian",
"iu": "Inuktitut",
"ja": "Japanese",
"jv": "Javanese",
"ka": "Georgian",
"kg": "Kongo",
"ki": "Kikuyu; Gikuyu",
"kj": "Kuanyama; Kwanyama",
"kk": "Kazakh",
"kl": "Kalaallisut; Greenlandic",
"km": "Central Khmer",
"kn": "Kannada",
"ko": "Korean",
"kr": "Kanuri",
"ks": "Kashmiri",
"ku": "Kurdish",
"kv": "Komi",
"kw": "Cornish",
"ky": "Kirghiz; Kyrgyz",
"la": "Latin",
"lb": "Luxembourgish; Letzeburgesch",
"lg": "Ganda",
"li": "Limburgan; Limburger; Limburgish",
"ln": "Lingala",
"lo": "Lao",
"lt": "Lithuanian",
"lu": "Luba-Katanga",
"lv": "Latvian",
"mg": "Malagasy",
"mh": "Marshallese",
"mi": "Maori",
"mk": "Macedonian",
"ml": "Malayalam",
"mn": "Mongolian",
"mr": "Marathi",
"ms": "Malay",
"mt": "Maltese",
"my": "Burmese",
"na": "Nauru",
"nb": "Bokmål, Norwegian; Norwegian Bokmål",
"nd": "Ndebele, North; North Ndebele",
"ne": "Nepali",
"ng": "Ndonga",
"nl": "Dutch; Flemish",
"nn": "Norwegian Nynorsk; Nynorsk, Norwegian",
"no": "Norwegian",
"nr": "Ndebele, South; South Ndebele",
"nv": "Navajo; Navaho",
"ny": "Chichewa; Chewa; Nyanja",
"oc": "Occitan (post 1500)",
"oj": "Ojibwa",
"om": "Oromo",
"or": "Oriya",
"os": "Ossetian; Ossetic",
"pa": "Panjabi; Punjabi",
"pi": "Pali",
"pl": "Polish",
"ps": "Pushto; Pashto",
"pt": "Portuguese",
"qu": "Quechua",
"rm": "Romansh",
"rn": "Rundi",
"ro": "Romanian; Moldavian; Moldovan",
"ru": "Russian",
"rw": "Kinyarwanda",
"sa": "Sanskrit",
"sc": "Sardinian",
"sd": "Sindhi",
"se": "Northern Sami",
"sg": "Sango",
"si": "Sinhala; Sinhalese",
"sk": "Slovak",
"sl": "Slovenian",
"sm": "Samoan",
"sn": "Shona",
"so": "Somali",
"sq": "Albanian",
"sr": "Serbian",
"ss": "Swati",
"st": "Sotho, Southern",
"su": "Sundanese",
"sv": "Swedish",
"sw": "Swahili",
"ta": "Tamil",
"te": "Telugu",
"tg": "Tajik",
"th": "Thai",
"ti": "Tigrinya",
"tk": "Turkmen",
"tl": "Tagalog",
"tn": "Tswana",
"to": "Tonga (Tonga Islands)",
"tr": "Turkish",
"ts": "Tsonga",
"tt": "Tatar",
"tw": "Twi",
"ty": "Tahitian",
"ug": "Uighur; Uyghur",
"uk": "Ukrainian",
"ur": "Urdu",
"uz": "Uzbek",
"ve": "Venda",
"vi": "Vietnamese",
"vo": "Volapük",
"wa": "Walloon",
"wo": "Wolof",
"xh": "Xhosa",
"yi": "Yiddish",
"yo": "Yoruba",
"za": "Zhuang; Chuang",
"zh": "Chinese",
"zu": "Zulu"
}[code];
}
}
/**