diff --git a/.gitignore b/.gitignore index c022ab1..9d14fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ nosetests.xml # js ts_dist/ node_modules +web/ui.js diff --git a/package-lock.json b/package-lock.json index 50b0fdc..fc11483 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,14 @@ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, + "@types/jquery": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.0.tgz", + "integrity": "sha512-C7qQUjpMWDUNYQRTXsP5nbYYwCwwgy84yPgoTT7fPN69NH92wLeCtFaMsWeolJD1AF/6uQw3pYt62rzv83sMmw==", + "requires": { + "@types/sizzle": "*" + } + }, "@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", @@ -60,6 +68,19 @@ "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", "dev": true }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==" + }, + "@types/typeahead": { + "version": "0.11.32", + "resolved": "https://registry.npmjs.org/@types/typeahead/-/typeahead-0.11.32.tgz", + "integrity": "sha512-5NkqKPwkBh2dGxFEJjc4Vt9/XsaGmx+tfXMfcJxWjPvCvvCFEKRCz+bz7ZfRAiO0e0Hh1foBol3anLzMY1Ea8A==", + "requires": { + "@types/jquery": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.5.0.tgz", @@ -1379,6 +1400,11 @@ "esutils": "^2.0.2" } }, + "dom": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/dom/-/dom-0.0.2.tgz", + "integrity": "sha1-4V+3WV4ym9Enj8yFjQ97jPOOS1E=" + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -2807,9 +2833,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.17.tgz", + "integrity": "sha512-/B2DjOphAoqi5BX4Gg2oh4UR0Gy/A7xYAMh3aSECEKzwS3eCDEpS0Cals1Ktvxwlal3bBJNc+5W9kNIcADdw5Q==", "dev": true }, "log-symbols": { @@ -4480,6 +4506,22 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typeahead": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typeahead/-/typeahead-0.2.2.tgz", + "integrity": "sha1-FADg8oLOZ9M5j8NKYu1eGCTFkps=", + "requires": { + "dom": "0.0.2", + "xtend": "1.0.3" + }, + "dependencies": { + "xtend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-1.0.3.tgz", + "integrity": "sha1-P12Tc1PM7Y4IU5mlY/2yJUHClgo=" + } + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", diff --git a/package.json b/package.json index a57ee76..78b8cd5 100644 --- a/package.json +++ b/package.json @@ -34,5 +34,9 @@ "typescript": "^3.9.6", "webpack": "^4.43.0", "webpack-cli": "^3.3.12" + }, + "dependencies": { + "@types/typeahead": "^0.11.32", + "typeahead": "^0.2.2" } } diff --git a/ts_src/ui.ts b/ts_src/ui.ts new file mode 100644 index 0000000..50adf67 --- /dev/null +++ b/ts_src/ui.ts @@ -0,0 +1,75 @@ +import {replacements} from './data'; +import {replace} from './replace'; + +const latexInput = $('#latexInput'); +const unicodeOutput = $('#unicodeOutput'); +const permaLink = $('.permalink'); + + +// copy replacements and sort case insensitive +var listOfReplacements = replacements + .map(function(r) { return { value: r[0], unicode: r[1] }; }) + .sort(function(a, b) { return a.value.toLowerCase().localeCompare(b.value.toLowerCase()); }); + +// reorder such that when just typing '\' suggestions starting with '\a' appear +var indexOfFirstA = listOfReplacements + .map(function(r) { return r.value.slice(0, 2).toLowerCase(); }) + .indexOf('\\a'); +listOfReplacements = listOfReplacements.slice(indexOfFirstA).concat(listOfReplacements.slice(0, indexOfFirstA)); + + +// Bloodhound: the source (dataset) for typeahead +var mySource = new Bloodhound({ + datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.value); }, + queryTokenizer: function(d) { var i = d.lastIndexOf('\\'); var r = [d.substr(0, i), d.substr(i)]; console.log(r); return r; }, + local: listOfReplacements, +}); +mySource.initialize(); + +// apply typeahead to input text field +latexInput.typeahead({ + hint: false, + highlight: true, + minLength: 0, +}, { + display: 'value', + source: mySource.ttAdapter(), + limit: 15, + templates: { + suggestion: function(datum: {unicode:string, value:string}) { + return '
'+datum.unicode+''+datum.value+'
'; + }, + }, +}); + + +latexInput.on('typeahead:selected', function (object, datum) { + $('input#unicodeOutput').val(datum.unicode); +}); +// $('.tt-query').css('background-color','#fff'); +latexInput.focus(); + + +// click handler for LaTeX examples +$('.latexExample').on('click', function(e) { + latexInput + .typeahead('val', $(this).html()) + .trigger('change') + .focus(); +}); + +// generate unicode output and permalink +latexInput.on('keyup change', function(e) { + const input = $(this).val(); + unicodeOutput.val(replace(input)); + permaLink.attr('href', 'https://www.unicodeit.net/?'+encodeURIComponent(input)); +}); + + +if (location.search) { + var latex = decodeURIComponent(location.search.slice(1)); + latexInput + .typeahead('val', latex) + .trigger('change') + .focus(); +} diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..4c164f1 --- /dev/null +++ b/web/index.html @@ -0,0 +1,106 @@ + + + + + + + unicodeit.net + + + + + + + + +
+
+
+

unicode.net β testing

+
+ +
+

Online Demo

+
+
+ +
+
+ +
+
+

+ +

+
+
+ +
+
+

Created by Sven Kreiss and Kyle Cranmer.
+ Maintained on GitHub.

+ + +

+ + + + +

+ + + +
+
+ +

+ + + + +

Help

+ +

Type LaTeX expressions in the input box on the left. The converted text in the box on the right can be copied to most programs including PowerPoint and Keynote, e-mail clients like Outlook and Mail and even apps on smart phones. UnicodeIt can create greek letters, arrows, mathematical symbols and many more for presentations, emails, chats, facebook, etc.

+ +
+ +
Examples
+ + + + + + + + +
x \in (-\infty, \infty)x ∈ (-∞, ∞)
p\bar{p} \to \mu^+\mu^-pp̅ → μ⁺μ⁻
\alpha\omega\epsilon\S\om\inαωε§øm∈
^2H ^6Li ^{10}B ^{14}N²H ⁶Li ¹⁰B ¹⁴N
\mathcal{L} \mathcal{H} \mathbb{R} \mathbb{C}ℒ ℋ ℝ ℂ
+
+ +
+
+



+
+ + + + + + + + + diff --git a/web/style.css b/web/style.css new file mode 100644 index 0000000..3185035 --- /dev/null +++ b/web/style.css @@ -0,0 +1,103 @@ + body { + font-family: "Helvetica Neue", helvetica, arial; + padding: 30px 15px 30px 15px; + color:#999; + } + + h1 { color:#55C; margin-top:0; } + .banner_it { color:#000; } + h1 small { font-size:40%; } + + .leftColumn, .rightColumn { margin-top:50px; } + + #content_manage { display:none; } + #content_participate { display:none; } + + .subtitle { font-size:50%; color:#888; } + .beta { font-size:40%; color:#ccc; } + + .large_stat { font-size:300%; } + .raw { word-wrap:break-word; } + + .footer { color:#ccc; font-size:80%; } + .footer a { color:#ccc; } + + ul { + list-style: none; + margin: 0; + padding: 0; + } + + ul li { + line-height: 1.4; + } + + input { margin:5px; } + .latexExample { + cursor:pointer; + max-width:9em; + overflow:hidden; + text-overflow:ellipsis; + } + + .permalink { + display: inline-block; + margin: 1em 1em 1em 0.5em; + font-size: 85%; + } + + + + + + +.twitter-typeahead{ + width:100%; +} + +.twitter-typeahead .tt-query, +.twitter-typeahead .tt-hint { + margin-bottom: 0; +} +.tt-menu { + min-width: 160px; + margin-top: 2px; + padding: 5px 0; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); + box-shadow: 0 5px 10px rgba(0,0,0,.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + width:100%; +} + +.tt-suggestion { + display: block; + padding: 3px 20px; +} + +.tt-suggestion:hover { + cursor: pointer; + color: #fff; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0) +} + +.tt-suggestion p { + margin: 0; +} diff --git a/webpack.config.js b/webpack.config.js index 7a117e1..f4dbc4a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,6 @@ const path = require('path'); -module.exports = { +module.exports = [{ mode: 'development', entry: './ts_src/replace.ts', devtool: 'inline-source-map', @@ -20,4 +20,24 @@ module.exports = { filename: 'unicodeit.js', path: path.resolve(__dirname, 'ts_dist'), }, -}; +}, { + mode: 'development', + entry: './ts_src/ui.ts', + devtool: 'inline-source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [ '.tsx', '.ts', '.js' ], + }, + output: { + filename: 'ui.js', + path: path.resolve(__dirname, 'web'), + }, +}];