浏览代码

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

n1474335 7 年之前
父节点
当前提交
6947d2a7f3
共有 5 个文件被更改,包括 266 次插入31 次删除
  1. 19 19
      src/core/ChefWorker.js
  2. 40 4
      src/core/FlowControl.js
  3. 2 2
      src/core/Utils.js
  4. 1 1
      src/core/config/OperationConfig.js
  5. 204 5
      src/core/lib/Magic.js

+ 19 - 19
src/core/ChefWorker.js

@@ -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.
  *

+ 40 - 4
src/core/FlowControl.js

@@ -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);
+
+        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>`;
+
+        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}`;
+            }
 
-        const specExec = await magic.speculativeExecution(depth+1);
-        const output = JSON.stringify(specExec, null, 2);
+            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>`;
+        });
 
-        dish.set(output, Dish.STRING);
+        output += "</table>";
+        dish.set(output, Dish.HTML);
         return state;
     },
 

+ 2 - 2
src/core/Utils.js

@@ -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 = "",

+ 1 - 1
src/core/config/OperationConfig.js

@@ -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: [
             {

+ 204 - 5
src/core/lib/Magic.js

@@ -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];
+    }
+
 }
 
 /**