Polish
authorKris Kowal <kris.kowal@cixar.com>
Sun, 14 Oct 2012 23:41:28 +0000 (16:41 -0700)
committerKris Kowal <kris.kowal@cixar.com>
Sun, 14 Oct 2012 23:41:28 +0000 (16:41 -0700)
-   Update to use latest Bindings.
-   Test Mode of Beleriand against the Doors of Durin, provide it as an example
    in the mode selector.
    -   Adjust alternate forms of I to favor those most common on the Doors of
        Durin.
-   Support doubling vowels in all modes, as an alternative to expressing them
    with accents.
-   Update About page.

14 files changed:
beleriand.js
classical.js
editor/about.html
editor/app.css [moved from editor/index.css with 100% similarity]
editor/index.html
editor/index.js
editor/modes.html
editor/modes.js
editor/page.css [moved from editor/modes.css with 95% similarity]
general-use.js
spec/beleriand-spec.js [new file with mode: 0644]
spec/beleriand.js [new file with mode: 0644]
spec/classical.js
spec/general-use.js

index fbbf45b..f163908 100644 (file)
@@ -1,6 +1,9 @@
 
+// TODO parse following "w"
+
 var TengwarParmaite = require("./tengwar-parmaite");
 var Parser = require("./parser");
+var Notation = require("./notation");
 var makeDocumentParser = require("./document-parser");
 var normalize = require("./normalize");
 var punctuation = require("./punctuation");
@@ -234,10 +237,10 @@ function parseTengwa(callback, options) {
                             return callback(makeColumn("short-carrier"));
                         } else if (primes === 1) {
                             return callback(makeColumn("short-carrier").addAbove("i"));
+                        } else if (primes === 2) {
+                            return callback(makeColumn("long-carrier").addAbove("i"));
                         } else if (primes === 3) {
                             return callback(makeColumn("long-carrier"));
-                        } else if (primes === 4) {
-                            return callback(makeColumn("long-carrier").addAbove("i"));
                         } else {
                             return callback(makeColumn("long-carrier").addAbove("i").addError("I only has four variants between short or long and dotted or not."));
                         }
index 6ecab9e..d8a44e7 100644 (file)
@@ -407,7 +407,9 @@ function parseTehta(callback, options, previous) {
     return function (character) {
         if (character === "a") {
             return function (character) {
-                if (character === "i") {
+                if (character === "a") {
+                    return parseTehta(callback, options, previous)("á");
+                } else if (character === "i") {
                     return callback([previous, makeColumn("yanta").addAbove("a")]);
                 } else if (character === "u") {
                     return callback([previous, makeColumn("ure").addAbove("a")]);
@@ -420,7 +422,9 @@ function parseTehta(callback, options, previous) {
         } else if (character === "e") {
             var tehta = swapDotSlash("e", options);
             return function (character) {
-                if (character === "u") {
+                if (character === "e") {
+                    return parseTehta(callback, options, previous)("é");
+                } else if (character === "u") {
                     return callback([previous, makeColumn("ure").addAbove(tehta)]);
                 } else if (previous && previous.canAddAbove("e")) {
                     return callback([previous.addAbove(tehta)])(character);
@@ -431,7 +435,9 @@ function parseTehta(callback, options, previous) {
         } else if (character === "i") {
             var iTehta = swapDotSlash("i", options);
             return function (character) {
-                if (character === "u") {
+                if (character === "i") {
+                    return parseTehta(callback, options, previous)("í");
+                } else if (character === "u") {
                     if (options.iuRising) {
                         return callback([previous, makeColumn("anna").addAbove(reverseCurls("u", options)).addBelow("y")]);
                     } else {
@@ -445,7 +451,9 @@ function parseTehta(callback, options, previous) {
             };
         } else if (character === "o") {
             return function (character) {
-                if (character === "i") {
+                if (character === "o") {
+                    return parseTehta(callback, options, previous)("ó");
+                } else if (character === "i") {
                     return callback([previous, makeColumn("yanta").addAbove(reverseCurls("o", options))]);
                 } else if (previous && previous.canAddAbove("o")) {
                     return callback([previous.addAbove(reverseCurls("o", options))])(character);
@@ -455,7 +463,9 @@ function parseTehta(callback, options, previous) {
             };
         } else if (character === "u") {
             return function (character) {
-                if (character === "i") {
+                if (character === "u") {
+                    return parseTehta(callback, options, previous)("ú");
+                } else if (character === "i") {
                     return callback([previous, makeColumn("yanta").addAbove("u")]);
                 } else if (previous && previous.canAddAbove("u")) {
                     return callback([previous.addAbove(reverseCurls("u", options))])(character);
index c7559ed..fb38b1d 100644 (file)
@@ -1,20 +1,21 @@
 <html>
     <head>
         <meta http-equiv="content-type" content="text/html; charset=utf-8">
-        <style>
-            body {
-                width: 40em;
-            }
-        </style>
+        <link rel="stylesheet" type="text/css" href="page.css">
     </head>
-    <body>
+    <body><div>
+
+        <h1>Tengwar Transcriber</h1>
 
         <p><em><strong>Usage:</strong></em>
-        <a href="index.html">This</a> is a live transcriber from our Latin phonetic
-        alphabet to the elvish Tengwar alphabet.  Type a message in the bottom
-        text area and it will be displayed in the equivalent elvish letters
-        in the area above.  Copy the link in the address bar and send
-        your message to friends and colleagues.</p>
+        <a href="index.html">This</a> is a live transcriber from our Latin
+        phonetic alphabet to the Elvish Tengwar alphabet.  Type a message in
+        the bottom text area and it will be displayed in the equivalent Elvish
+        letters in the area above.  Copy the link in the address bar and send
+        your message to friends and colleagues.  Copy the Wiki text and paste
+        the Tengwar Markup block into an article on a Wiki that
+        <a href="#support-wiki-markup">supports</a> this Tengwar
+        Transcriber.</p>
 
         <p><em><strong>Compatibility:</strong></em> The transcriber may not
         work in some browsers because I used fancy early-draft features of some
         <p>However, there is a much older
         <a href="http://tengwar.art.pl/tengwar/ott/start.php?l=en">tengwar transcriber</a>
         that should suffice in the interim.</p>
+
+        <a name="wiki-markup-support"></a>
+        <p><em><strong>Wiki Markup Support:</strong></em> At present, no wikis
+        support Tengwar transcription with this tool.  If you maintain a
+        MediaWiki installation and would like to add support for this tool,
+        I’ve published an open-source
+        <a href="https://github.com/kriskowal/tengwarjs/blob/master/mediawiki/tengwar.php">MediaWiki extension</a>.
+        Install that PHP script and incorporate the transcriber’s static assets
+        in your page, which include <code>tengwar.min.js</code>, and the
+        <code>css</code>, <code>ttf</code>, <code>eot</code>, <code>svg</code>,
+        <code>woff</code> files for <code>tengwar-annatar</code> and
+        <code>tengwar-parmaite</code> from
+        <a href="https://github.com/kriskowal/tengwarjs">TengwarJS</a>.</p>
         
         <p><em><strong>Purpose:</strong></em> The transcriber is suitable for
         rendering <a href="http://tolkiengateway.net/wiki/Sindarin">Sindarin</a> in
         more or the
         <a href="http://at.mansbjorkman.net/teng_beleriand.htm">Mode&nbsp;of&nbsp;Beleriand</a>,
         and <a href="http://tolkiengateway.net/wiki/Quenay">Quenya</a> in the
-        <a href="http://at.mansbjorkman.net/teng_quenya.htm">Classical</a> mode.</p>
-
-        <p>The General Use mode is suitable for showing how Sindarin might
-        have been written and attempts to be consistent with
-        <em>The King’s Letter</em> from J.R.R.&nbsp;Tolkien’s book,
-        <a href="http://tolkiengateway.net/wiki/Sauron_Defeated">Sauron&nbsp;Defeated</a>,
-        I used the same mode for my
-        <a href="http://3rin.gs/"><nobr>Map of Middle-earth</nobr></a>.</p>
-
-        <p>I’ve posted some <a href="../samples/index.html">sample
-        transcriptions</a> in various modes.</p>
+        <a href="http://at.mansbjorkman.net/teng_quenya.htm">Classical</a>
+        mode.  The <a href="modes.html">Mode Selector</a> provides information,
+        options, and instructions for comparing Tengwar modes, fonts, and the
+        supported languages.</p>
 
         <p><em><strong>Technical details:</strong></em>
         The transcriber gives you the option of using either the italic of
@@ -54,7 +61,7 @@
         in the untypable regions of a single font because of technical
         limitations of rendering fonts in web browsers.  Particularly, a font
         cannot paint outside its container, so a tehta rendered alone in a span
-        would be rendered invisible.
+        would be invisible.
 
         <p><em>Tengwar Annatar’s</em> italic resembles the <em>Ring
         Inscription</em> from <em>The Lord of the Rings</em>, presumably in
@@ -72,7 +79,7 @@
         alphabet, Johan’s Annatar provides some glyphs in an alternate font.</p>
 
         <p>Because of limitations in rendering text on the web and the
-        peculiarities of elvish fonts, it was not possible to alternate between
+        peculiarities of Elvish fonts, it was not possible to alternate between
         the normal and alternate fonts in this application.  Instead, I’ve
         created a custom version of the font that incorporates the alternate
         glyphs I needed for this mode using code points that are not so
@@ -81,7 +88,7 @@
         rotated U-hook tehtar that were less prone to overlap with the
         underlying tengwar.</p>
 
-        <p>The particular limitation is that diacritics in elvish have no width
+        <p>The particular limitation is that diacritics in Elvish have no width
         and are drawn before the cursor. Some of the diacritics I use are in
         the alternate font.  To put an alternate diacritic over the previous
         tengwa, consonant, glyph, you have to put it in a
         precisely where they need to be or providing ligatures for difficult
         placements.</p>
 
-        <p>In the interim, I’ve engaged in the process of manually verifying
-        the placement and use of diacritics for every
-        combination of tengwa and tehta for
-        <a href="../combinations/tengwar-parmaite.html">both</a>
-        <a href="../combinations/tengwar-annatar.html">fonts</a></p>
-
-        <p>If you take a look under the hood, the transcriber is a CommonJS
-        module, suitable for use both with a NodeJS package available in NPM as
-        <code>tengwar</code> and usable as a browser script with the minified
-        bundle.</p>
+        <p>If you take a look
+        <a href="https://github.com/kriskowal/tengwarjs">under the bonnet</a>,
+        the transcriber is a CommonJS module, suitable for use both with a
+        NodeJS package available in NPM as <code>tengwar</code> and usable as a
+        browser script with the minified bundle.</p>
 
         <p>Further work might include:</p>
 
             <li>More modes of transcription, particularly phonetic and
             orthographic English and Quenya.</li>
             <li>Tie-in with IPA dictionaries of various languages</li>
-            <li>Tie-in with elvish dictionary for special-cases</li>
+            <li>Tie-in with Elvish dictionary for special-cases</li>
             <li>Display intermediate encoding that shows the names of each
             tengwa and tehta in every cluster.</li>
             <li>Search the dictionary, encyclopedia, and map.</li>
             })();
         </script>
 
-    </body>
+    </div></body>
 </html>
similarity index 100%
rename from editor/index.css
rename to editor/app.css
index 081c6c1..527d8f5 100644 (file)
@@ -4,9 +4,9 @@
         <title>Tengwar Transcriber</title>
         <link rel="stylesheet" type="text/css" href="node_modules/tengwar/tengwar-parmaite.css">
         <link rel="stylesheet" type="text/css" href="node_modules/tengwar/tengwar-annatar.css">
-        <link rel="stylesheet" type="text/css" href="index.css">
+        <link rel="stylesheet" type="text/css" href="app.css">
     </head>
-    <body class="annatar">
+    <body>
 
         <noscript>
             <br>This transcriber makes extensive use
index 39a2f1c..ec7d4b8 100644 (file)
@@ -51,78 +51,82 @@ var bindings = Bindings.create(null, {
     },
 
     "heightPx": {
-        dependencies: "state.height",
-        get: function () {
-            return this.state.height + "px";
+        "<-": "state.height",
+        convert: function (height) {
+            return height + "px";
         },
-        set: function (height) {
-            this.state.height = parseInt(height, 10);
+        revert: function (height) {
+            return parseInt(height, 10);
         }
     },
 
     "mode": {
-        dependencies: "state.mode",
-        get: function () {
-            return modes[this.state.mode] || modes['general-use'];
+        args: ["state.mode"],
+        compute: function (mode) {
+            return modes[mode] || modes['general-use'];
         }
     },
 
     "font": {
-        dependencies: "state.font",
-        get: function () {
-            return fonts[this.state.font] || modes['annatar'];
+        args: ["state.font"],
+        compute: function (font) {
+            return fonts[font] || modes['annatar'];
         }
     },
 
-    "transcription": {
-        dependencies: ["mode", "font", "state.q"],
-        get: function () {
+    "outputElement.innerHTML": {
+        args: ["mode", "font", "state.q", "state.options"],
+        compute: function (mode, font, input, flags) {
             var options = {
-                font: this.font,
+                font: font,
                 block: true
             };
-            this.state.options.forEach(function (flag) {
+            flags.forEach(function (flag) {
                 flag = flag.replace(/\-(\w)/g, function ($, $1) {
                     return $1.toUpperCase();
                 });
                 options[flag] = true;
             });
-            return this.mode.transcribe(this.state.q, options);
+            return mode.transcribe(input, options);
         }
     },
 
-    "outputElement.innerHTML": {
-        "<-": "transcription"
+    "selectModeElement.href": {
+        "<-": "'modes.html' + searchString"
     },
 
     "searchString": {
-        dependencies: ["state.q", "state.mode", "state.font", "state.height", "state.options"],
-        get: function () {
-            if (!this.state)
-                return;
-            delete this.state[""]; // XXX QS bug interprets empty array as & and back to an empty assignment
+        args: ["state.q", "state.mode", "state.font", "state.height", "state.options"],
+        compute: function () {
+            var state = this.state;
+
+            // remove things that QS can't seem to handle
+            delete state[""]; // XXX QS bug interprets empty array as & and back to an empty assignment
+            if (state.options.length == 0) {
+                delete state.options;
+            }
             var string = "?" + QS.stringify(this.state);
             window.history.replaceState(this.state, "", string);
+
+            state.options = state.options || [];
             return string;
         }
     },
 
-    "selectModeElement.href": {
-        "<-": "'modes.html' + searchString"
+    "wikiTextElement.href": {
+        "<-": "'data:text/plain;charset=utf-8,' + wikiText"
     },
 
     "wikiText": {
-        "dependencies": ["state.q", "state.mode", "state.font", "state.height", "state.options"],
-        get: function () {
-            var text = this.state.q;
-            var font = this.state.font;
-            var mode = [this.state.mode].concat(this.state.options).filter(Boolean).join(" ");
+        args: ["state.q", "state.mode", "state.font", "state.height", "state.options"],
+        compute: function (text, mode, font, height, flags) {
+            var modeLine = [mode].concat(flags).filter(Boolean).join(" ");
             if (/\n/.test(text)) {
                 return encodeURIComponent(
                     "<tengwarblock " +
                     "font=\"" + font + "\" " +
                     "mode=\"" + mode + "\">\n" +
-                    text +
+                    encodeHtml(text) +
                     "\n</tengwarblock>"
                 );
             } else {
@@ -130,15 +134,11 @@ var bindings = Bindings.create(null, {
                     "<tengwar " +
                     "font=\"" + font + "\" " +
                     "mode=\"" + mode + "\">" +
-                    text +
+                    encodeHtml(text) +
                     "</tengwar>"
                 );
             }
         }
-    },
-
-    "wikiTextElement.href": {
-        "<-": "'data:text/plain,' + wikiText"
     }
 
 });
@@ -168,3 +168,10 @@ divider.addEventListener("mousedown", function (event) {
     }
 });
 
+function encodeHtml(string) {
+    return String(string)
+        .replace(/&/g, "&amp;")
+        .replace(/</g, "&lt;")
+        .replace(/>/g, "&gt;");
+}
+
index 25ab510..7ab4e47 100644 (file)
@@ -4,12 +4,14 @@
         <title>Tengwar Transcriber</title>
         <link rel="stylesheet" type="text/css" href="node_modules/tengwar/tengwar-parmaite.css">
         <link rel="stylesheet" type="text/css" href="node_modules/tengwar/tengwar-annatar.css">
-        <link rel="stylesheet" type="text/css" href="modes.css">
+        <link rel="stylesheet" type="text/css" href="page.css">
         <script src="node_modules/mr/bootstrap.js" data-module="modes"></script>
     </head>
     <body><div>
 
+
         <h1>Modes of the Tengwar</h1>
+        <h3><em>For a Tengar Transcriber</em></h3>
 
         <ul>
             <li><a href="#general-use">General Use Mode</a></li>
@@ -23,6 +25,7 @@
             </ul>
             <li><a href="#beleriand">Mode of Beleriand</a></li>
             <ul>
+                <li><a href="#doors-of-durin">Doors of Durin</a></li>
                 <li><a href="#kings-letter-beleriand">The First Version of the King’s Letter</a></li>
             </ul>
         </ul>
@@ -36,7 +39,8 @@
         <h3>As in the third version of <em><a class="dagger" href="http://tolkiengateway.net/wiki/King's_Letter">The King’s Letter</a></em></h3>
 
         <div>
-            <span class="dynamic-tengwar"
+            <span id="kings-letter-general-use-tengwar"
+                class="dynamic-tengwar"
                 data-tengwar="Elessar Telcontar: Aragorn Arathornion Edhelharn, aran Gondor ar Hîr i Mbair Annui, anglennatha i Varanduiniant erin dolothen Ethuil, egor ben genediad Drannail erin Gwirith edwen."
                 data-mode="general-use"
                 data-font="parmaite">
@@ -69,9 +73,9 @@
         <h3>As in <em><a class="dagger" href="http://tolkiengateway.net/wiki/Ring_Verse">The One Ring Inscription</a></em></h3>
 
         <div>
-            <span class="dynamic-tengwar"
-                data-tengwar=">ashnazgdurbatulûk, ashnazggimbatul<
-                ashnazgthrakatulûk, aghburzumishikrimpatul"
+            <span id="black-speech-tengwar"
+                class="dynamic-tengwar"
+                data-tengwar=">ashnazgdurbatulûk, ashnazggimbatul<&#10;ashnazgthrakatulûk, aghburzumishikrimpatul"
                 data-mode="general-use black-speech"
                 data-font="annatar">
                 Rendering&hellip; (Requires JavaScript and Web Fonts)
         <h3>As in <em><a class="dagger" href="http://tolkiengateway.net/wiki/Nam%C3%A1ri%C3%AB">The Namárië Poem</a></em></h3>
 
         <div>
-            <span class="dynamic-tengwar"
-                data-tengwar="Ai! laurië lantar lassi súrinen,
-                Yéni únótimë ve rámar aldaron!"
+            <span id="namarie-tengwar"
+                class="dynamic-tengwar"
+                data-tengwar="Ai! laurië lantar lassi súrinen,&#10;Yéni únótimë ve rámar aldaron!"
                 data-mode="classical"
                 data-font="parmaite">
             </span>
         <hr>
 
 
+        <a name="doors-of-durin"></a>
+        <h2><a class="dagger" href="http://at.mansbjorkman.net/teng_beleriand.htm">Mode of Beleriand</a></h2>
+        <h3>As on the <em><a class="dagger" href="http://tolkiengateway.net/wiki/Doors_of_Durin">Doors of Durin</a></em>, Moria</h3>
+
+        <div>
+            <span id="doors-of-durin-tengwar"
+                class="dynamic-tengwar"
+                data-tengwar="Ennyn Durin Aran Mori'a - Pedo Mellon a Mi''nno.&#10;Im Narvi hain echant. Celebri''mbor o Eregi'on teithant i'' thi'w hi'n."
+                data-mode="beleriand"
+                data-font="parmaite">
+                Rendering&hellip; (Requires JavaScript and Web Fonts)
+            </span>
+            <button id="doors-of-durin-button">Select</button>
+        </div>
+
+        <blockquote>
+            Ennyn Durin Aran Moria &mdash; Pedo Mellon a Minno.<br>
+            Im Narvi hain echant. Celebrimbor o Eregion teithant i thiw hin.
+        </blockquote>
+
+        <blockquote>
+            The Doors of Durin, Lord of Moria &mdash; Speak, friend, and enter.<br>
+            I, Narvi, made them. Celebrimbor of Hollin drew these signs.
+        </blockquote>
+
+        <p>The Mode of Beleriand uses a tengwa for nearly every vowel
+        <em>and</em> consonant.  Diacritics (marks) are applied to lengthen
+        vowels, merge vowels into diphthongs (glides), or precede a consonant
+        with the corresponding nasal, like NT and MB.</p>
+
+        <p>This particular example, from <em>The Fellowship of the Ring</em>
+        uses inconsistent variations of the letter I.  An I can be represented
+        by a short or long carrier, with or without a dot above, and three
+        variants are used in the attested inscription.  0&ndash;3 tick marks
+        after an I tell the transcriber which variant to use.</p>
+
+
+        <hr>
+
+
         <a name="kings-letter-beleriand"></a>
         <h2><a class="dagger" href="http://at.mansbjorkman.net/teng_beleriand.htm">Mode of Beleriand</a></h2>
         <h3>As in the first version of <em><a class="dagger" href="http://tolkiengateway.net/wiki/King's_Letter">The King’s Letter</a></em></h3>
 
         <div>
-            <span class="dynamic-tengwar"
+            <span id="kings-letter-beleriand-tengwar"
+                class="dynamic-tengwar"
                 data-tengwar="Elessar Telcontar: Aragorn Arathornion Edhelharn, aran Gondor ar Hîr i Mbair Annui, anglennatha i Varanduiniant erin dolothen Ethuil, egor ben genediad Drannail erin Gwirith edwen."
                 data-mode="beleriand"
                 data-font="parmaite">
         <a name="general-use"></a>
         <h2><a class="dagger" href="http://at.mansbjorkman.net/teng_quenya.htm">General Use Mode</a></h2>
 
+        <p>General Use Mode uses diacritics to represent vowels, usually above
+        the tengwa the vowel precedes.  General Use mode is suitable for
+        representing most languages and was used mostl colloquial languages in
+        Middle-earth in the Third Age.</p>
+
         <table id="general-use-table">
 
             <tr><th colspan="3">Font</th></tr>
                 <td>
                     <td><span class="general-use-font" data-tengwar="
                         rats cats'
-                        bats'', rats''',
+                        bats'' rats'''
                         cats'''' bats'''''
                     " data-mode="general-use"></span>
                 <td>
                     </td>
             </tr>
 
+            <tr><th colspan="3">Long Vowels</th></tr>
+            <tr>
+                <td>
+                    <td><span class="general-use-font" data-tengwar="aa ee ii oo uu" data-mode="beleriand"></span>
+                </td>
+                <td>
+                    Long vowels can be expressed by doubling the vowel.
+                    Although Tolkien would never have expressed a
+                    transliteration to the Latin alphabet in that way, the
+                    transcriber tollerates it as a convenience for typists who
+                    are not familiar with how to use accents with their
+                    keyboard.
+                </td>
+            </tr>
+
             <tr><th colspan="3">Orthographic E</th></tr>
             <tr>
                 <td>
                 <td><span class="general-use-font" data-tengwar="love'" data-mode="general-use"></span>
                 <td><code>love'</code>, silent E for orthographic English
-                (transliterated by letter instead of sound).
+                (transliterated by letter instead of sound).</td>
             </tr>
 
         </table>
         <a name="classical"></a>
         <h2><a class="dagger" href="http://at.mansbjorkman.net/teng_quenya.htm">Classical Mode</a></h2>
 
+        <p>Classical Mode uses diacritics for vowels, usually above the tengwa
+        they <em>follow</em>.  The mode is optimal for expressing
+        <a class="dagger" href="http://tolkiengateway.net/wiki/Quenya">Quenya</a>,
+        the elder language of the Elves, but does not provide the full palette
+        of sounds necessary for many other languages including English and
+        Sindarin.</p>
+
         <table>
 
             <tr><th colspan="3">Font</th></tr>
             <tr>
                 <td>
                     <input type="radio"
-                        id="classical-falling-iu"
+                        id="classical-iu-falling"
                         name="classical-iu"
                         checked>
                 </td>
                 </td>
             </tr>
 
+            <tr><th colspan="3">Long Vowels</th></tr>
+            <tr>
+                <td>
+                    <td><span class="classical-font" data-tengwar="aa ee ii oo uu" data-mode="beleriand"></span>
+                </td>
+                <td>
+                    Long vowels can be expressed by doubling the vowel.
+                    Although Tolkien would never have expressed a
+                    transliteration to the Latin alphabet in that way, the
+                    transcriber tollerates it as a convenience for typists who
+                    are not familiar with how to use accents with their
+                    keyboard.
+                </td>
+            </tr>
+
 
         </table>
 
         <a name="beleriand"></a>
         <h2><a class="dagger" href="http://at.mansbjorkman.net/teng_beleriand.htm">Mode of Beleriand</a></h2>
 
+        <p>The Mode of Beleriand uses a tengwa for nearly every vowel
+        <em>and</em> consonant.  Diacritics (marks) are applied to lengthen
+        vowels, merge vowels into diphthongs (glides), or precede a consonant
+        with the corresponding nasal, like NT and MB, but full tengwa are used
+        for most vowels and consonants.</p>
+
         <table>
 
             <tr><th colspan="3">Font</th></tr>
                 </td>
             </tr>
 
+            <tr><th colspan="3">Long Vowels</th></tr>
+            <tr>
+                <td>
+                    <td><span class="beleriand-font" data-tengwar="aa ee ii oo uu yy" data-mode="beleriand"></span>
+                </td>
+                <td>
+                    Long vowels can be expressed by doubling the vowel.
+                    Although Tolkien would never have expressed a
+                    transliteration to the Latin alphabet in that way, the
+                    transcriber tollerates it as a convenience for typists who
+                    are not familiar with how to use accents with their
+                    keyboard.
+                </td>
+            </tr>
+
+            <tr><th colspan="3">Variations of I</th></tr>
+            <tr>
+                <td>
+                    <td><span class="beleriand-font" data-tengwar="
+                        i i' i'' i'''
+                        ii ii'
+                    " data-mode="beleriand"></span>
+                </td>
+                <td>
+                    <code>i i' i'' i'''</code><br>
+                    <code>í í'</code> <em>or</em> <code>ii ii'</code>
+                </td>
+            </tr>
 
         </table>
 
         <p><button id="beleriand-button">Select</button></p>
 
 
+
+        <script src="http://static.getclicky.com/js" type="text/javascript"></script> 
+        <script type="text/javascript">clicky.init(237383);</script> 
+        <noscript><p><img alt="Clicky" width="1" height="1" src="http://in.getclicky.com/237383ns.gif" /></p></noscript> 
+
+        <script type="text/javascript">
+            var _gaq = _gaq || [];
+            _gaq.push(['_setAccount', 'UA-25808349-1']);
+            _gaq.push(['_setDomainName', '.3rin.gs']);
+            _gaq.push(['_trackPageview']);
+            (function() {
+                var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+                ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+                var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+            })();
+        </script>
+
     </div></body>
 </html>
index 50c11f7..31e94a6 100644 (file)
@@ -13,14 +13,14 @@ if (window.location.search) {
         q: "",
         mode: "general-use",
         font: "annatar",
-        options: [],
         height: void 0
     }
 }
 
-if (inputState.q === "Mae Govannen, Arda!") {
+if (inputState.q === "Mae govannen, Arda!") {
     inputState.q = "";
 }
+inputState.options = inputState.options || [];
 
 var traceAll = false;
 
@@ -52,30 +52,26 @@ function TengwarComponent(element, state) {
         },
 
         "mode": {
-            "dependencies": "state.mode",
-            "get": function () {
-                return modes[this.state.mode];
+            "args": ["state.mode"],
+            "compute": function (mode) {
+                return modes[mode];
             }
         },
 
-        "font": {
-            "dependencies": "state.font",
-            "get": function () {
-                return fonts[this.state.font];
+        "options.font": {
+            "args": ["state.font"],
+            "compute": function (font) {
+                return fonts[font];
             }
         },
 
-        "options.font": {"<-": "font"},
-
-        "transcription": {
-            "dependencies": ["options.font", "mode", "input"],
-            "get": function () {
-                var options = this.mode.makeOptions(this.options);
-                return this.mode.transcribe(this.input, options);
+        "element.innerHTML": {
+            "args": ["options.font", "mode", "input", "options"],
+            "compute": function (font, mode, input, options) {
+                var options = mode.makeOptions(options);
+                return mode.transcribe(input, options);
             }
-        },
-
-        "element.innerHTML": {"<-": "transcription"},
+        }
 
     });
 }
@@ -130,28 +126,35 @@ var states = {};
 // template modes
 
 Button(document.getElementById("kings-letter-general-use-button"), {
-    q: inputState.q || "Elessar Telcontar: Aragorn Arathornion Edhelharn, aran Gondor ar Hîr i Mbair Annui, anglennatha i Varanduiniant erin dolothen Ethuil, egor ben genediad Drannail erin Gwirith edwen.",
+    q: document.getElementById("kings-letter-general-use-tengwar").dataset.tengwar,
     font: "parmaite",
     mode: "general-use",
     options: []
 });
 
 Button(document.getElementById("kings-letter-beleriand-button"), {
-    q: inputState.q || "Elessar Telcontar: Aragorn Arathornion Edhelharn, aran Gondor ar Hîr i Mbair Annui, anglennatha i Varanduiniant erin dolothen Ethuil, egor ben genediad Drannail erin Gwirith edwen.",
+    q: document.getElementById("kings-letter-beleriand-tengwar").dataset.tengwar,
+    font: "parmaite",
+    mode: "beleriand",
+    options: []
+});
+
+Button(document.getElementById("doors-of-durin-button"), {
+    q: document.getElementById("doors-of-durin-tengwar").dataset.tengwar,
     font: "parmaite",
     mode: "beleriand",
     options: []
 });
 
 Button(document.getElementById("black-speech-button"), {
-    q: inputState.q || ">ashnazgdurbatulûk, ashnazggimbatul<\nashnazgthrakatulûk, aghburzumishikrimpatul",
+    q: document.getElementById("black-speech-tengwar").dataset.tengwar,
     font: "annatar",
     mode: "general-use",
     options: ["black-speech"]
 });
 
 Button(document.getElementById("namarie-button"), {
-    q: inputState.q || "Ai! laurië lantar lassi súrinen,\nYéni únótimë ve rámar aldaron!",
+    q: document.getElementById("namarie-tengwar").dataset.tengwar,
     font: "parmaite",
     mode: "classical",
     options: []
@@ -303,6 +306,8 @@ Bindings.create(null, {
 ["general-use", "classical", "beleriand"].forEach(function (mode) {
     var state = states[mode];
     state.q = inputState.q || "";
-    state.options.swap(0, states[mode].options.length, inputState.options);
+    state.options.swap(0, states[mode].options.length, inputState.options.filter(function (option) {
+        return option && option !== "undefined";
+    }));
 });
 
similarity index 95%
rename from editor/modes.css
rename to editor/page.css
index 56f0927..6be5181 100644 (file)
@@ -18,12 +18,7 @@ h1, h2, h3, h4, h5, h6 {
 }
 
 a {
-    text-decoration: none;
-    color: #700;
-}
-
-a.dagger:after {
-    content: "†";
+    color: #a00;
 }
 
 table {
@@ -42,6 +37,11 @@ td .tengwar {
     white-space: nowrap;
 }
 
+label {
+    display: block;
+    cursor: pointer;
+}
+
 hr {
     border: solid #500 1px;
 }
index 7b90446..eaa6b72 100644 (file)
@@ -156,7 +156,7 @@ function parseColumn(callback, options, previous) {
                         // put the previous tehta over the appropriate carrier
                         // then follow up with this tengwa.
                         return parseTengwaAnnotations(function (column) {
-                            return callback([makeCarrier(tehta), column]);
+                            return callback([makeCarrier(tehta, options), column]);
                         }, column);
                     }
                 } else {
@@ -173,7 +173,7 @@ function parseColumn(callback, options, previous) {
                 }
                 return parseTengwaAnnotations(function (carrier) {
                     return callback([carrier]);
-                }, makeCarrier(tehta));
+                }, makeCarrier(tehta, options));
             } else {
                 return function (character) {
                     if (Parser.isBreak(character)) {
@@ -190,18 +190,33 @@ function parseColumn(callback, options, previous) {
         }, options, tehta);
     });
 
-    function makeCarrier(tehta) {
-        if (shorterVowels[tehta]) {
-            return makeColumn("long-carrier").addAbove(shorterVowels[tehta]);
-        } else {
-            return makeColumn("short-carrier").addAbove(tehta);
-        }
+}
+
+function makeCarrier(tehta, options) {
+    var font = options.font;
+    var makeColumn = font.makeColumn;
+    if (tehta === "á") {
+        return makeColumn("wilya").addAbove("a");
+    } else if (shorterVowels[tehta]) {
+        return makeColumn("long-carrier").addAbove(shorterVowels[tehta]);
+    } else {
+        return makeColumn("short-carrier").addAbove(tehta);
     }
 }
 
 function parseTehta(callback) {
     return function (character) {
-        if (tengwaTehtar.indexOf(character) !== -1) {
+        if (character === "") {
+            return callback();
+        } else if (lengthenableVowels.indexOf(character) !== -1) {
+            return function (nextCharacter) {
+                if (nextCharacter === character) {
+                    return callback(longerVowels[character]);
+                } else {
+                    return callback(character)(nextCharacter);
+                }
+            };
+        } else if (nonLengthenableVowels.indexOf(character) !== -1) {
             return callback(character);
         } else {
             return callback()(character);
@@ -209,14 +224,17 @@ function parseTehta(callback) {
     };
 }
 
-var tengwaTehtar = "aeiouóú";
+var lengthenableVowels = "aeiou";
+var longerVowels = {"a": "á", "e": "é", "i": "í", "o": "ó", "u": "ú"};
+var nonLengthenableVowels = "aeióú";
+var tehtarThatCanBeAddedAbove = "aeiouóú";
 var vowels = "aeiouáéíóú";
 var shorterVowels = {"á": "a", "é": "e", "í": "i", "ó": "o", "ú": "u"};
 var reverseCurls = {"o": "u", "u": "o", "ó": "ú", "ú": "ó"};
 var swapDotSlash = {"i": "e", "e": "i"};
 
 function canAddAboveTengwa(tehta) {
-    return tengwaTehtar.indexOf(tehta) !== -1;
+    return tehtarThatCanBeAddedAbove.indexOf(tehta) !== -1;
 }
 
 function parseTengwa(callback, options, tehta) {
@@ -501,10 +519,8 @@ function parseTengwa(callback, options, tehta) {
         } else if (character === "y") {
             return callback(makeColumn("wilya").addBelow("y"));
             // TODO consider alt: return callback(makeColumn("long-carrier").addAbove("i"));
-        } else if (character === "á") {
-            return callback(makeColumn("wilya").addAbove("a"));
-        } else if (shorterVowels[character] && tengwaTehtar.indexOf(character) == -1) {
-            return callback(makeColumn("long-carrier").addAbove(shorterVowels[character]));
+        } else if (shorterVowels[character]) {
+            return callback(makeCarrier(character, options).addAbove(shorterVowels[character]));
         } else {
             return callback()(character);
         }
diff --git a/spec/beleriand-spec.js b/spec/beleriand-spec.js
new file mode 100644 (file)
index 0000000..e3be6a6
--- /dev/null
@@ -0,0 +1,13 @@
+
+var Beleriand = require("../beleriand");
+var tests = require("./beleriand");
+
+describe("beleriand", function () {
+    Object.keys(tests).forEach(function (input) {
+        it("should encode " + input, function () {
+            expect(Beleriand.encode(input, {
+            })).toEqual(tests[input]);
+        });
+    });
+});
+
diff --git a/spec/beleriand.js b/spec/beleriand.js
new file mode 100644 (file)
index 0000000..a2eedad
--- /dev/null
@@ -0,0 +1,24 @@
+module.exports = {
+
+    // Doors of Durin
+    "Ennyn": "yanta;numen;silme-nuquerna;ore",
+    "Durin": "ando;ure;romen;short-carrier;ore",
+    "Aran": "round-carrier;romen;round-carrier;ore",
+    "Mori'a": "vala;anna;romen;short-carrier:i;round-carrier",
+    "Pedo": "parma;yanta;ando;anna",
+    "Mellon": "vala;yanta;lambe;lambe;anna;ore",
+    "a": "round-carrier",
+    "Mi'nno.": "vala;short-carrier:i;numen;anna;full-stop",
+    "Im": "short-carrier;vala",
+    "Narvi": "ore;round-carrier;romen;ampa;short-carrier",
+    "hain": "hyarmen;round-carrier:í;ore",
+    "echant.": "yanta;harma;round-carrier;tinco:tilde-above;full-stop",
+    "Celebri''mbor": "calma;yanta;lambe;yanta;umbar;romen;long-carrier:i;umbar:tilde-above;anna;romen",
+    "o": "anna",
+    "Eregi'on": "yanta;romen;yanta;anga;short-carrier:i;anna;ore",
+    "teithant": "tinco;yanta:í;thule;round-carrier;tinco:tilde-above",
+    "i''": "long-carrier:i",
+    "thiw": "thule;short-carrier;wilya",
+    "hi'n.": "hyarmen;short-carrier:i;ore;full-stop"
+
+};
index a6aa8fa..c648c59 100644 (file)
@@ -5,15 +5,27 @@ module.exports = {
     "hlóce": "halla;lambe:ó;calma:e", // attested
     "hríve": "halla;romen;long-carrier:i;vala:e", // attested
     "hyarmen": "hyarmen:a,y;ore;malta:e;numen",
-    //"hyarmen(classical)": "hyarmen:a;romen;malta:e;numen",
-    //"hyarmen(classical,aha)": "hyarmen:a,y;romen;malta:e;numen",
     "lambe": "lambe:a;umbar:e",
     "moria": "malta:o;romen:i;short-carrier:a",
     "namárië": "numen:a;malta;long-carrier:a;romen:i;short-carrier:e",
+    "namaarië": "numen:a;malta;long-carrier:a;romen:i;short-carrier:e",
     "silme": "silme-nuquerna:i;lambe;malta:e",
     "syi": "silme:y;short-carrier:i", //*
     "thúlë": "thule:ú;lambe:e",
     "tyelpe": "tinco:e,y;lambe;parma:e",
     "vanwa": "vala:a;numen;wilya:a",
     "yuldar": "anna:u,y;alda:a;ore",
+
+    // long vowels
+    "á": "long-carrier:a",
+    "aa": "long-carrier:a",
+    "é": "long-carrier:e",
+    "ee": "long-carrier:e",
+    "í": "long-carrier:i",
+    "ii": "long-carrier:i",
+    "ó": "long-carrier:o",
+    "oo": "long-carrier:o",
+    "ú": "long-carrier:u",
+    "uu": "long-carrier:u"
+
 };
index 4d4f035..3fe6359 100644 (file)
@@ -59,6 +59,18 @@ module.exports = {
     "allys": "lambe:a,y,s-final,tilde-below",
     "alyssa": "lambe:a,y;silme:tilde-below;short-carrier:a",
     "ls": "lambe:s-final",
-    "ls'": "lambe:s-flourish"
+    "ls'": "lambe:s-flourish",
+
+    // long vowels
+    "á": "wilya:a",
+    "aa": "wilya:a",
+    "é": "long-carrier:e",
+    "ee": "long-carrier:e",
+    "í": "long-carrier:i",
+    "ii": "long-carrier:i",
+    "ó": "long-carrier:o",
+    "oo": "long-carrier:o",
+    "ú": "long-carrier:u",
+    "uu": "long-carrier:u"
 
 };