diff --git a/Makefile b/Makefile index fde1f31..cb700e0 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,9 @@ -ISSO_JS_SRC := $(shell find isso/js/app -type f) $(shell ls isso/js/*.js | grep -vE "(min|dev)") -ISSO_JS_DST := isso/js/embed.min.js isso/js/embed.dev.js isso/js/count.min.js isso/js/count.dev.js +ISSO_JS_SRC := $(shell find isso/js/app -type f) \ + $(shell ls isso/js/*.js | grep -vE "(min|dev)") \ + isso/js/lib/requirejs-jade/jade.js + +ISSO_JS_DST := isso/js/embed.min.js isso/js/embed.dev.js \ + isso/js/count.min.js isso/js/count.dev.js ISSO_CSS := isso/css/isso.css @@ -14,7 +18,7 @@ CSS := docs/_static/css/site.css all: man js site init: - (cd isso/js; bower install almond requirejs requirejs-text) + (cd isso/js; bower install almond requirejs requirejs-text jade) isso/js/%.min.js: $(ISSO_JS_SRC) $(ISSO_CSS) r.js -o isso/js/build.$*.js out=$@ diff --git a/docs/docs/install.rst b/docs/docs/install.rst index 1444ea1..d55091a 100644 --- a/docs/docs/install.rst +++ b/docs/docs/install.rst @@ -127,8 +127,6 @@ way to set up Isso. It requires a lot more dependencies and effort: - Virtualenv - SQLite 3.3.8 or later - a working C compiler -- Ruby 1.8 or higher -- a `SCSS `_ compiler - Node.js, `NPM `__ and `Bower `__ Get a fresh copy of Isso: @@ -152,12 +150,6 @@ Install Isso and its dependencies: ~> python setup.py develop # or `install` ~> isso run -Compilation from SCSS to CSS: - -.. code-block:: sh - - ~> make css - Install JavaScript modules: .. code-block:: sh @@ -175,7 +167,7 @@ Optimization: .. code-block:: sh - ~> npm install -g requirejs uglifyjs + ~> npm install -g requirejs uglifyjs jade ~> make js Init scripts diff --git a/isso/js/app/count.js b/isso/js/app/count.js index d1ecd2a..f29ac4e 100644 --- a/isso/js/app/count.js +++ b/isso/js/app/count.js @@ -1,4 +1,4 @@ -define(["app/api", "app/dom", "app/markup"], function(api, $, Mark) { +define(["app/api", "app/dom", "app/i18n"], function(api, $, i18n) { return function() { var objs = {}; @@ -28,9 +28,7 @@ define(["app/api", "app/dom", "app/markup"], function(api, $, Mark) { var index = urls.indexOf(key); for (var i = 0; i < objs[key].length; i++) { - objs[key][i].textContent = Mark.up( - "{{ i18n-num-comments | pluralize : `n` }}", - {n: rv[index]}); + objs[key][i].textContent = i18n.pluralize("num-comments", rv[index]); } } } diff --git a/isso/js/app/i18n.js b/isso/js/app/i18n.js index a59e731..cc841d0 100644 --- a/isso/js/app/i18n.js +++ b/isso/js/app/i18n.js @@ -2,43 +2,67 @@ define(["app/config", "app/i18n/de", "app/i18n/en", "app/i18n/fr", "app/i18n/ru" "use strict"; - // pluralization functions for each language you support - var plurals = { - "en": function(msgs, n) { - return msgs[n === 1 ? 0 : 1]; - }, - "fr": function(msgs, n) { - return msgs[n > 1 ? 1 : 0] - }, - "ru": function(msg, n) { - if (n % 10 === 1 && n % 100 !== 11) { - return msg[0]; - } else if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) { - return msg[1]; - } else { - return msg[2] !== undefined ? msg[2] : msg[1]; - } + var pluralforms = function(lang) { + switch (lang) { + case "en": + case "de": + case "it": + return function(msgs, n) { + return msgs[n === 1 ? 0 : 1]; + }; + case "fr": + return function(msgs, n) { + return msgs[n > 1 ? 1 : 0]; + }; + case "ru": + return function(msgs, n) { + if (n % 10 === 1 && n % 100 !== 11) { + return msgs[0]; + } else if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) { + return msgs[1]; + } else { + return typeof msgs[2] !== "undefined" ? msgs[2] : msgs[1]; + } + }; + default: + return null; } }; - plurals["de"] = plurals["en"]; - plurals["it"] = plurals["en"]; - // useragent's prefered language (or manually overridden) var lang = config.lang; // fall back to English - if (!plurals[lang]) { + if (! pluralforms(lang)) { lang = "en"; } - return { - plurals: plurals, - lang: lang, + var catalogue = { de: de, en: en, fr: fr, ru: ru, it: it }; + + var plural = pluralforms(lang); + + var translate = function(msgid) { + return catalogue[lang][msgid] || en[msgid] || "???"; + }; + + var pluralize = function(msgid, n) { + var msg; + + msg = translate(msgid); + msg = plural(msg.split("\n"), (+ n)); + + return msg ? msg.replace("{{ n }}", (+ n)) : msg; + }; + + return { + lang: lang, + translate: translate, + pluralize: pluralize + }; }); diff --git a/isso/js/app/isso.js b/isso/js/app/isso.js index 69e8f8d..e3ca1b9 100644 --- a/isso/js/app/isso.js +++ b/isso/js/app/isso.js @@ -1,15 +1,13 @@ /* Isso – Ich schrei sonst! */ -define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/markup", "app/i18n", "app/lib", "app/globals"], - function(templates, $, utils, config, api, Mark, i18n, lib, globals) { +define(["app/dom", "app/utils", "app/config", "app/api", "app/jade", "app/i18n", "app/lib", "app/globals"], + function($, utils, config, api, jade, i18n, lib, globals) { "use strict"; - var msgs = i18n[i18n.lang]; - var Postbox = function(parent) { - var el = $.htmlify(Mark.up(templates["postbox"])); + var el = $.htmlify(jade.render("postbox")); if (config["avatar"]) { // add a default identicon to not waste CPU cycles @@ -95,7 +93,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m entrypoint = $("#isso-" + commentWrapper.id + " > .text-wrapper > .isso-follow-up"); commentWrapper.name = commentWrapper.id; } - var el = $.htmlify(Mark.up(templates["comment_loader"], commentWrapper)); + var el = $.htmlify(jade.render("comment_loader", {"comment": commentWrapper})); entrypoint.append(el); @@ -134,7 +132,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m }; var insert = function(comment, scrollIntoView) { - var el = $.htmlify(Mark.up(templates["comment"], comment)); + var el = $.htmlify(jade.render("comment", {"comment": comment})); // update datetime every 60 seconds var refresh = function() { @@ -173,11 +171,11 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m form = footer.insertAfter(new Postbox(comment.parent === null ? comment.id : comment.parent)); form.onsuccess = function() { toggler.next(); }; $(".textarea", form).focus(); - $("a.reply", footer).textContent = msgs["comment-close"]; + $("a.reply", footer).textContent = i18n.translate("comment-close"); }, function() { form.remove(); - $("a.reply", footer).textContent = msgs["comment-reply"]; + $("a.reply", footer).textContent = i18n.translate("comment-reply"); } ); @@ -211,8 +209,8 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m function(toggler) { var edit = $("a.edit", footer); - edit.textContent = msgs["comment-save"]; - edit.insertAfter($.new("a.cancel", msgs["comment-cancel"])).on("click", function() { + edit.textContent = i18n.translate("comment-save"); + edit.insertAfter($.new("a.cancel", i18n.translate("comment-cancel"))).on("click", function() { toggler.canceled = true; toggler.next(); }); @@ -253,7 +251,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m text.classList.add("text"); $("a.cancel", footer).remove(); - $("a.edit", footer).textContent = msgs["comment-edit"]; + $("a.edit", footer).textContent = i18n.translate("comment-edit"); } ); @@ -262,9 +260,9 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m var del = $("a.delete", footer); var state = ! toggler.state; - del.textContent = msgs["comment-confirm"]; + del.textContent = i18n.translate("comment-confirm"); del.on("mouseout", function() { - del.textContent = msgs["comment-delete"]; + del.textContent = i18n.translate("comment-delete"); toggler.state = state; del.onmouseout = null; }); @@ -275,12 +273,12 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m if (rv) { el.remove(); } else { - $("span.note", header).textContent = msgs["comment-deleted"]; + $("span.note", header).textContent = i18n.translate("comment-deleted"); text.innerHTML = "

 

"; $("a.edit", footer).remove(); $("a.delete", footer).remove(); } - del.textContent = msgs["comment-delete"]; + del.textContent = i18n.translate("comment-delete"); }); } ); diff --git a/isso/js/app/jade.js b/isso/js/app/jade.js new file mode 100644 index 0000000..78346b6 --- /dev/null +++ b/isso/js/app/jade.js @@ -0,0 +1,62 @@ +define(["libjs-jade-runtime", "app/utils", "jade!app/text/postbox", "jade!app/text/comment", "jade!app/text/comment-loader"], function(runtime, utils, tt_postbox, tt_comment, tt_comment_loader) { + "use strict"; + + var globals = {}, + templates = {}; + + var load = function(name, js) { + templates[name] = (function(jade) { + var fn; + eval("fn = " + js); + return fn; + })(runtime); + }; + + var set = function(name, value) { + globals[name] = value; + }; + + load("postbox", tt_postbox); + load("comment", tt_comment); + load("comment-loader", tt_comment_loader); + + set("bool", function(arg) { return arg ? true : false; }); + set("datetime", function(date) { + if (typeof date !== "object") { + date = new Date(parseInt(date, 10) * 1000); + } + + return Array.join([ + date.getUTCFullYear(), + utils.pad(date.getUTCMonth(), 2), + utils.pad(date.getUTCDay(), 2)], "-"); + }); + + return { + "set": set, + "render": function(name, locals) { + var rv, t = templates[name]; + if (! t) { + throw new Error("Template not found: '" + name + "'"); + } + + locals = locals || {}; + + var keys = []; + for (var key in locals) { + if (locals.hasOwnProperty(key) && !globals.hasOwnProperty(key)) { + keys.push(key); + globals[key] = locals[key]; + } + } + + rv = templates[name](globals); + + for (var i = 0; i < keys.length; i++) { + delete globals[keys[i]]; + } + + return rv; + } + }; +}); \ No newline at end of file diff --git a/isso/js/app/lib/editor.js b/isso/js/app/lib/editor.js index ef49e9d..d2fe542 100644 --- a/isso/js/app/lib/editor.js +++ b/isso/js/app/lib/editor.js @@ -1,4 +1,5 @@ -define(["app/dom", "app/markup"], function($, Mark) { +define(["app/dom", "app/i18n"], function($, i18n) { + "use strict"; return function(el) { @@ -13,7 +14,7 @@ define(["app/dom", "app/markup"], function($, Mark) { el.on("blur", function() { if (el.textContent.length === 0) { - el.textContent = Mark.up("{{ i18n-postbox-text }}"); + el.textContent = i18n.translate("postbox-text"); el.classList.add("placeholder"); } }); diff --git a/isso/js/app/markup.js b/isso/js/app/markup.js index 37890d3..98461a1 100644 --- a/isso/js/app/markup.js +++ b/isso/js/app/markup.js @@ -2,11 +2,6 @@ define(["vendor/markup", "app/config", "app/i18n", "app/text/svg"], function(Mar "use strict"; - var pad = function(n, width, z) { - z = z || '0'; - n = n + ''; - return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; - }; // circumvent https://github.com/adammark/Markup.js/issues/22 function merge(obj) { @@ -38,10 +33,6 @@ define(["vendor/markup", "app/config", "app/i18n", "app/text/svg"], function(Mar return parseInt(a, 10) - parseInt(b, 10); }; - Mark.pipes.pluralize = function(str, n) { - return i18n.plurals[i18n.lang](str.split("\n"), +n).trim(); - }; - var strip = function(string) { // allow whitespace between Markup.js delimiters such as // {{ var | pipe : arg }} instead of {{var|pipe:arg}} diff --git a/isso/js/app/text/comment-loader.html b/isso/js/app/text/comment-loader.html deleted file mode 100644 index 2e37e4b..0000000 --- a/isso/js/app/text/comment-loader.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/isso/js/app/text/comment-loader.jade b/isso/js/app/text/comment-loader.jade new file mode 100644 index 0000000..926417e --- /dev/null +++ b/isso/js/app/text/comment-loader.jade @@ -0,0 +1,2 @@ +div(class='isso-comment-loader' id='isso-loader-#{comment.name}') + a(class='load_hidden' href='#') #{pluralize('comment-hidden', comment.hidden_replies)} diff --git a/isso/js/app/text/comment.html b/isso/js/app/text/comment.html deleted file mode 100644 index 18623c9..0000000 --- a/isso/js/app/text/comment.html +++ /dev/null @@ -1,57 +0,0 @@ -
- {{ if conf-avatar | bool }} -
- -
- {{ /if }} -
-
- {{ if website | bool }} - - {{ author | blank : `i18n-comment-anonymous` }} - - {{ else }} - - {{ author | blank : `i18n-comment-anonymous` }} - - {{ /if }} - - - - - - - {{ if mode | equals : 2 }} - {{ i18n-comment-queued }} - {{ /if }} - {{ if mode | equals : 4 }} - {{ i18n-comment-deleted }} - {{ /if }} - - -
-
- {{ if mode | equals : 4 }} -

 

- {{ else }} - {{ text }} - {{ /if }} -
- -
-
-
-
\ No newline at end of file diff --git a/isso/js/app/text/comment.jade b/isso/js/app/text/comment.jade new file mode 100644 index 0000000..1167d5b --- /dev/null +++ b/isso/js/app/text/comment.jade @@ -0,0 +1,37 @@ +div(class='isso-comment' id='isso-#{comment.id}') + if conf.avatar + div(class='avatar') + svg(data-hash='#{comment.hash}') + div(class='text-wrapper') + div(class='isso-comment-header' role='meta') + if bool(comment.website) + a(class='author' href='#{comment.website}' rel='nofollow') + = bool(comment.author) ? comment.author : i18n('comment-anonymous') + else + span(class='author') + = bool(comment.author) ? comment.author : i18n('comment-anonymous') + span(class="spacer") • + a(class='permalink' href='#isso-#{comment.id}') + date(datetime='#{datetime(comment.created)}') + span(class='note') + = comment.mode == 2 ? i18n('comment-queued') : comment.mode == 4 ? i18n('comment-deleted') : '' + + div(class='text') + if comment.mode == 4 + p   + else + != comment.text + + div(class='isso-comment-footer') + if comment.likes - comment.dislikes != 0 + span(class='votes') #{comment.likes - comment.dislikes} + a(class='upvote' href='#') + i!= svg['arrow-up'] + span(class='spacer') | + a(class='downvote' href='#') + i!= svg['arrow-down'] + a(class='reply' href='#') #{i18n('comment-reply')} + a(class='edit' href='#') #{i18n('comment-edit')} + a(class='delete' href='#') #{i18n('comment-delete')} + + div(class='isso-follow-up') diff --git a/isso/js/app/text/html.js b/isso/js/app/text/html.js deleted file mode 100644 index cf2c865..0000000 --- a/isso/js/app/text/html.js +++ /dev/null @@ -1,7 +0,0 @@ -define(["text!./postbox.html", "text!./comment.html", "text!./comment-loader.html"], function (postbox, comment, comment_loader) { - return { - postbox: postbox, - comment: comment, - comment_loader: comment_loader - }; -}); diff --git a/isso/js/app/text/postbox.html b/isso/js/app/text/postbox.html deleted file mode 100644 index d1bc5e7..0000000 --- a/isso/js/app/text/postbox.html +++ /dev/null @@ -1,23 +0,0 @@ -
- {{ if conf-avatar | bool }} -
- -
- {{ /if }} -
-
-
{{ i18n-postbox-text }}
-
-
-

- -

-

- -

-

- -

-
-
-
\ No newline at end of file diff --git a/isso/js/app/text/postbox.jade b/isso/js/app/text/postbox.jade new file mode 100644 index 0000000..5740da0 --- /dev/null +++ b/isso/js/app/text/postbox.jade @@ -0,0 +1,15 @@ +div(class='postbox') + if conf.avatar + div(class='avatar') + svg(class='blank' data-hash='') + div(class='form-wrapper') + div(class='textarea-wrapper') + div(class='textarea placeholder' contenteditable='true') + = i18n('postbox-text') + section(class='auth-section') + p(class='input-wrapper') + input(type='text' name='author' placeholder=i18n('postbox-author')) + p(class='input-wrapper') + input(type='email' name='email' placeholder=i18n('postbox-email')) + p(class='post-action') + input(type='submit' value=i18n('postbox-submit')) diff --git a/isso/js/app/utils.js b/isso/js/app/utils.js index a7bbe59..d49e60b 100644 --- a/isso/js/app/utils.js +++ b/isso/js/app/utils.js @@ -1,4 +1,4 @@ -define(["app/markup"], function(Mark) { +define(["app/i18n"], function(i18n) { "use strict"; // return `cookie` string if set @@ -6,6 +6,12 @@ define(["app/markup"], function(Mark) { return (document.cookie.match('(^|; )' + cookie + '=([^;]*)') || 0)[2]; }; + var pad = function(n, width, z) { + z = z || '0'; + n = n + ''; + return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; + }; + var ago = function(localTime, date) { var secs = ((localTime.getTime() - date.getTime()) / 1000); @@ -17,27 +23,19 @@ define(["app/markup"], function(Mark) { var mins = Math.ceil(secs / 60), hours = Math.ceil(mins / 60), days = Math.ceil(hours / 24); - var i18n = function(msgid, n) { - if (! n) { - return Mark.up("{{ i18n-" + msgid + " }}"); - } else { - return Mark.up("{{ i18n-" + msgid + " | pluralize : `n` }}", {n: n}); - } - }; - - return secs <= 45 && i18n("date-now") || - secs <= 90 && i18n("date-minute", 1) || - mins <= 45 && i18n("date-minute", mins) || - mins <= 90 && i18n("date-hour", 1) || - hours <= 22 && i18n("date-hour", hours) || - hours <= 36 && i18n("date-day", 1) || - days <= 5 && i18n("date-day", days) || - days <= 8 && i18n("date-week", 1) || - days <= 21 && i18n("date-week", Math.ceil(days / 7)) || - days <= 45 && i18n("date-month", 1) || - days <= 345 && i18n("date-month", Math.ceil(days / 30)) || - days <= 547 && i18n("date-year", 1) || - i18n("date-year", Math.ceil(days / 365.25)); + return secs <= 45 && i18n.translate("date-now") || + secs <= 90 && i18n.pluralize("date-minute", 1) || + mins <= 45 && i18n.pluralize("date-minute", mins) || + mins <= 90 && i18n.pluralize("date-hour", 1) || + hours <= 22 && i18n.pluralize("date-hour", hours) || + hours <= 36 && i18n.pluralize("date-day", 1) || + days <= 5 && i18n.pluralize("date-day", days) || + days <= 8 && i18n.pluralize("date-week", 1) || + days <= 21 && i18n.pluralize("date-week", Math.ceil(days / 7)) || + days <= 45 && i18n.pluralize("date-month", 1) || + days <= 345 && i18n.pluralize("date-month", Math.ceil(days / 30)) || + days <= 547 && i18n.pluralize("date-year", 1) || + i18n.pluralize("date-year", Math.ceil(days / 365.25)); }; var text = function(html) { @@ -55,6 +53,7 @@ define(["app/markup"], function(Mark) { return { cookie: cookie, + pad: pad, ago: ago, text: text, detext: detext diff --git a/isso/js/build.embed.js b/isso/js/build.embed.js index bf4d244..0eae344 100644 --- a/isso/js/build.embed.js +++ b/isso/js/build.embed.js @@ -1,7 +1,7 @@ ({ baseUrl: ".", mainConfigFile: 'config.js', - stubModules: ['text'], + stubModules: ['text', 'jade'], name: "components/almond/almond", include: ['embed'], diff --git a/isso/js/config.js b/isso/js/config.js index 70e6408..da225b6 100644 --- a/isso/js/config.js +++ b/isso/js/config.js @@ -1,6 +1,9 @@ var requirejs = { paths: { - text : "components/requirejs-text/text", + "text": "components/requirejs-text/text", + "jade": "lib/requirejs-jade/jade", + "libjs-jade": "components/jade/jade", + "libjs-jade-runtime": "components/jade/runtime" }, config: { diff --git a/isso/js/embed.js b/isso/js/embed.js index 6e6d8de..4328356 100644 --- a/isso/js/embed.js +++ b/isso/js/embed.js @@ -3,10 +3,15 @@ * Distributed under the MIT license */ -require(["app/lib/ready", "app/config", "app/api", "app/isso", "app/count", "app/dom", "app/markup", "app/text/css"], function(domready, config, api, isso, count, $, Mark, css) { +require(["app/lib/ready", "app/config", "app/i18n", "app/api", "app/isso", "app/count", "app/dom", "app/text/css", "app/text/svg", "app/jade"], function(domready, config, i18n, api, isso, count, $, css, svg, jade) { "use strict"; + jade.set("conf", config); + jade.set("i18n", i18n.translate); + jade.set("pluralize", i18n.pluralize); + jade.set("svg", svg); + domready(function() { if (config["css"]) { @@ -31,7 +36,7 @@ require(["app/lib/ready", "app/config", "app/api", "app/isso", "app/count", "app config["max-comments-nested"]).then( function(rv) { if (rv.total_replies === 0) { - $("#isso-thread > h4").textContent = Mark.up("{{ i18n-no-comments }}"); + $("#isso-thread > h4").textContent = i18n.translate("no-comments"); return; } @@ -44,7 +49,7 @@ require(["app/lib/ready", "app/config", "app/api", "app/isso", "app/count", "app } total_count = total_count + commentObject.total_replies; }); - $("#isso-thread > h4").textContent = Mark.up("{{ i18n-num-comments | pluralize : `n` }}", {n: total_count}); + $("#isso-thread > h4").textContent = i18n.pluralize("num-comments", total_count); if(rv.hidden_replies > 0) { isso.insert_loader(rv, lastcreated); diff --git a/isso/js/lib/requirejs-jade/jade.js b/isso/js/lib/requirejs-jade/jade.js new file mode 100644 index 0000000..b26ad55 --- /dev/null +++ b/isso/js/lib/requirejs-jade/jade.js @@ -0,0 +1,58 @@ +define(function() { + "use strict"; + + var jade = null, + builds = {}; + + var fetchText = function() { + throw new Error("Environment not supported."); + }; + + if (typeof process !== "undefined") { + var fs = require.nodeRequire("fs"); + var jade = require.nodeRequire("jade"); + fetchText = function(path, callback) { + callback(fs.readFileSync(path, "utf-8")); + }; + } else if ((typeof window !== "undefined" && window.navigator && window.document) || typeof importScripts !== "undefined") { + fetchText = function (url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + callback(xhr.responseText); + } + }; + xhr.send(null); + }; + } + + return { + + fetchText: fetchText, + + load: function(name, req, onload, config) { + var path = req.toUrl(name + ".jade"); + fetchText(path, function(text) { + if (jade === null) { + req(["libjs-jade"], function(jade) { + onload(jade.compileClient(text)); + onload(text); + }); + } else { + builds[name] = jade.compileClient(text); + onload(builds[name]); + } + }); + + }, + write: function(plugin, name, write) { + if (builds.hasOwnProperty(name)) { + write("define('" + plugin + "!" + name +"', function () {" + + " var fn = " + builds[name] + ";" + + " return fn;" + + "});\n"); + } + } + }; +}); diff --git a/isso/js/vendor/markup.js b/isso/js/vendor/markup.js deleted file mode 100644 index b3a915c..0000000 --- a/isso/js/vendor/markup.js +++ /dev/null @@ -1,476 +0,0 @@ -/* - Markup.js v1.5.16: http://github.com/adammark/Markup.js - MIT License - (c) 2011 - 2013 Adam Mark -*/ -var Mark = { - // Templates to include, by name. A template is a string. - includes: {}, - - // Global variables, by name. Global variables take precedence over context variables. - globals: {}, - - // The delimiter to use in pipe expressions, e.g. {{if color|like>red}}. - delimiter: ">", - - // Collapse white space between HTML elements in the resulting string. - compact: false, - - // Shallow-copy an object. - _copy: function (a, b) { - b = b || []; - - for (var i in a) { - b[i] = a[i]; - } - - return b; - }, - - // Get the value of a number or size of an array. This is a helper function for several pipes. - _size: function (a) { - return a instanceof Array ? a.length : (a || 0); - }, - - // This object represents an iteration. It has an index and length. - _iter: function (idx, size) { - this.idx = idx; - this.size = size; - this.length = size; - this.sign = "#"; - - // Print the index if "#" or the count if "##". - this.toString = function () { - return this.idx + this.sign.length - 1; - }; - }, - - // Pass a value through a series of pipe expressions, e.g. _pipe(123, ["add>10","times>5"]). - _pipe: function (val, expressions) { - var expression, parts, fn, result; - - // If we have expressions, pull out the first one, e.g. "add>10". - if ((expression = expressions.shift())) { - - // Split the expression into its component parts, e.g. ["add", "10"]. - parts = expression.split(this.delimiter); - - // Pull out the function name, e.g. "add". - fn = parts.shift().trim(); - - try { - // Run the function, e.g. add(123, 10) ... - result = Mark.pipes[fn].apply(null, [val].concat(parts)); - - // ... then pipe again with remaining expressions. - val = this._pipe(result, expressions); - } - catch (e) { - } - } - - // Return the piped value. - return val; - }, - - // TODO doc - _eval: function (context, filters, child) { - var result = this._pipe(context, filters), - ctx = result, - i = -1, - j, - opts; - - if (result instanceof Array) { - result = ""; - j = ctx.length; - - while (++i < j) { - opts = { - iter: new this._iter(i, j) - }; - result += child ? Mark.up(child, ctx[i], opts) : ctx[i]; - } - } - else if (result instanceof Object) { - result = Mark.up(child, ctx); - } - - return result; - }, - - // Process the contents of an IF or IF/ELSE block. - _test: function (bool, child, context, options) { - // Process the child string, then split it into the IF and ELSE parts. - var str = Mark.up(child, context, options).split(/\{\{\s*else\s*\}\}/); - - // Return the IF or ELSE part. If no ELSE, return an empty string. - return (bool === false ? str[1] : str[0]) || ""; - }, - - // Determine the extent of a block expression, e.g. "{{foo}}...{{/foo}}" - _bridge: function (tpl, tkn) { - var exp = "{{\\s*" + tkn + "([^/}]+\\w*)?}}|{{/" + tkn + "\\s*}}", - re = new RegExp(exp, "g"), - tags = tpl.match(re) || [], - t, - i, - a = 0, - b = 0, - c = -1, - d = 0; - - for (i = 0; i < tags.length; i++) { - t = i; - c = tpl.indexOf(tags[t], c + 1); - - if (tags[t].indexOf("{{/") > -1) { - b++; - } - else { - a++; - } - - if (a === b) { - break; - } - } - - a = tpl.indexOf(tags[0]); - b = a + tags[0].length; - d = c + tags[t].length; - - // Return the block, e.g. "{{foo}}bar{{/foo}}" and its child, e.g. "bar". - return [tpl.substring(a, d), tpl.substring(b, c)]; - } -}; - -// Inject a template string with contextual data and return a new string. -Mark.up = function (template, context, options) { - context = context || {}; - options = options || {}; - - // Match all tags like "{{...}}". - var re = /\{\{(.+?)\}\}/g, - // All tags in the template. - tags = template.match(re) || [], - // The tag being evaluated, e.g. "{{hamster|dance}}". - tag, - // The expression to evaluate inside the tag, e.g. "hamster|dance". - prop, - // The token itself, e.g. "hamster". - token, - // An array of pipe expressions, e.g. ["more>1", "less>2"]. - filters = [], - // Does the tag close itself? e.g. "{{stuff/}}". - selfy, - // Is the tag an "if" statement? - testy, - // The contents of a block tag, e.g. "{{aa}}bb{{/aa}}" -> "bb". - child, - // The resulting string. - result, - // The global variable being evaluated, or undefined. - global, - // The included template being evaluated, or undefined. - include, - // A placeholder variable. - ctx, - // Iterators. - i = 0, - j = 0; - - // Set custom pipes, if provided. - if (options.pipes) { - this._copy(options.pipes, this.pipes); - } - - // Set templates to include, if provided. - if (options.includes) { - this._copy(options.includes, this.includes); - } - - // Set global variables, if provided. - if (options.globals) { - this._copy(options.globals, this.globals); - } - - // Optionally override the delimiter. - if (options.delimiter) { - this.delimiter = options.delimiter; - } - - // Optionally collapse white space. - if (options.compact !== undefined) { - this.compact = options.compact; - } - - // Loop through tags, e.g. {{a}}, {{b}}, {{c}}, {{/c}}. - while ((tag = tags[i++])) { - result = undefined; - child = ""; - selfy = tag.indexOf("/}}") > -1; - prop = tag.substr(2, tag.length - (selfy ? 5 : 4)); - prop = prop.replace(/`(.+?)`/g, function (s, p1) { - return Mark.up("{{" + p1 + "}}", context); - }); - testy = prop.trim().indexOf("if ") === 0; - filters = prop.split("|"); - filters.shift(); // instead of splice(1) - prop = prop.replace(/^\s*if/, "").split("|").shift().trim(); - token = testy ? "if" : prop.split("|")[0]; - ctx = context[prop]; - - // If an "if" statement without filters, assume "{{if foo|notempty}}" - if (testy && !filters.length) { - filters = ["notempty"]; - } - - // Does the tag have a corresponding closing tag? If so, find it and move the cursor. - if (!selfy && template.indexOf("{{/" + token) > -1) { - result = this._bridge(template, token); - tag = result[0]; - child = result[1]; - i += tag.match(re).length - 1; // fast forward - } - - // Skip "else" tags. These are pulled out in _test(). - if (/^\{\{\s*else\s*\}\}$/.test(tag)) { - continue; - } - - // Evaluating a global variable. - else if ((global = this.globals[prop]) !== undefined) { - result = this._eval(global, filters, child); - } - - // Evaluating an included template. - else if ((include = this.includes[prop])) { - if (include instanceof Function) { - include = include(); - } - result = this._pipe(Mark.up(include, context), filters); - } - - // Evaluating a loop counter ("#" or "##"). - else if (prop.indexOf("#") > -1) { - options.iter.sign = prop; - result = this._pipe(options.iter, filters); - } - - // Evaluating the current context. - else if (prop === ".") { - result = this._pipe(context, filters); - } - - // Evaluating a variable with dot notation, e.g. "a.b.c" - else if (prop.indexOf(".") > -1) { - prop = prop.split("."); - ctx = Mark.globals[prop[0]]; - - if (ctx) { - j = 1; - } - else { - j = 0; - ctx = context; - } - - // Get the actual context - while (ctx && j < prop.length) { - ctx = ctx[prop[j++]]; - } - - result = this._eval(ctx, filters, child); - } - - // Evaluating an "if" statement. - else if (testy) { - result = this._pipe(ctx, filters); - } - - // Evaluating an array, which might be a block expression. - else if (ctx instanceof Array) { - result = this._eval(ctx, filters, child); - } - - // Evaluating a block expression. - else if (child) { - result = ctx ? Mark.up(child, ctx) : undefined; - } - - // Evaluating anything else. - else if (context.hasOwnProperty(prop)) { - result = this._pipe(ctx, filters); - } - - // Evaluating an "if" statement. - if (testy) { - result = this._test(result, child, context, options); - } - - // Replace the tag, e.g. "{{name}}", with the result, e.g. "Adam". - template = template.replace(tag, result === undefined ? "???" : result); - } - - return this.compact ? template.replace(/>\s+<") : template; -}; - -// Freebie pipes. See usage in README.md -Mark.pipes = { - empty: function (obj) { - return !obj || (obj + "").trim().length === 0 ? obj : false; - }, - notempty: function (obj) { - return obj && (obj + "").trim().length ? obj : false; - }, - blank: function (str, val) { - return !!str || str === 0 ? str : val; - }, - more: function (a, b) { - return Mark._size(a) > b ? a : false; - }, - less: function (a, b) { - return Mark._size(a) < b ? a : false; - }, - ormore: function (a, b) { - return Mark._size(a) >= b ? a : false; - }, - orless: function (a, b) { - return Mark._size(a) <= b ? a : false; - }, - between: function (a, b, c) { - a = Mark._size(a); - return a >= b && a <= c ? a : false; - }, - equals: function (a, b) { - return a == b ? a : false; - }, - notequals: function (a, b) { - return a != b ? a : false; - }, - like: function (str, pattern) { - return new RegExp(pattern, "i").test(str) ? str : false; - }, - notlike: function (str, pattern) { - return !Mark.pipes.like(str, pattern) ? str : false; - }, - upcase: function (str) { - return String(str).toUpperCase(); - }, - downcase: function (str) { - return String(str).toLowerCase(); - }, - capcase: function (str) { - return str.replace(/\b\w/g, function (s) { return s.toUpperCase(); }); - }, - chop: function (str, n) { - return str.length > n ? str.substr(0, n) + "..." : str; - }, - tease: function (str, n) { - var a = str.split(/\s+/); - return a.slice(0, n).join(" ") + (a.length > n ? "..." : ""); - }, - trim: function (str) { - return str.trim(); - }, - pack: function (str) { - return str.trim().replace(/\s{2,}/g, " "); - }, - round: function (num) { - return Math.round(+num); - }, - clean: function (str) { - return String(str).replace(/<\/?[^>]+>/gi, ""); - }, - size: function (obj) { - return obj.length; - }, - length: function (obj) { - return obj.length; - }, - reverse: function (arr) { - return Mark._copy(arr).reverse(); - }, - join: function (arr, separator) { - return arr.join(separator); - }, - limit: function (arr, count, idx) { - return arr.slice(+idx || 0, +count + (+idx || 0)); - }, - split: function (str, separator) { - return str.split(separator || ","); - }, - choose: function (bool, iffy, elsy) { - return !!bool ? iffy : (elsy || ""); - }, - toggle: function (obj, csv1, csv2, str) { - return csv2.split(",")[csv1.match(/\w+/g).indexOf(obj + "")] || str; - }, - sort: function (arr, prop) { - var fn = function (a, b) { - return a[prop] > b[prop] ? 1 : -1; - }; - return Mark._copy(arr).sort(prop ? fn : undefined); - }, - fix: function (num, n) { - return (+num).toFixed(n); - }, - mod: function (num, n) { - return (+num) % (+n); - }, - divisible: function (num, n) { - return num && (+num % n) === 0 ? num : false; - }, - even: function (num) { - return num && (+num & 1) === 0 ? num : false; - }, - odd: function (num) { - return num && (+num & 1) === 1 ? num : false; - }, - number: function (str) { - return parseFloat(str.replace(/[^\-\d\.]/g, "")); - }, - url: function (str) { - return encodeURI(str); - }, - bool: function (obj) { - return !!obj; - }, - falsy: function (obj) { - return !obj; - }, - first: function (iter) { - return iter.idx === 0; - }, - last: function (iter) { - return iter.idx === iter.size - 1; - }, - call: function (obj, fn) { - return obj[fn].apply(obj, [].slice.call(arguments, 2)); - }, - set: function (obj, key) { - Mark.globals[key] = obj; return ""; - }, - log: function (obj) { - console.log(obj); - return obj; - } -}; - -// Shim for IE. -if (typeof String.prototype.trim !== "function") { - String.prototype.trim = function() { - return this.replace(/^\s+|\s+$/g, ""); - } -} - -// Export for Node.js and AMD. -if (typeof module !== "undefined" && module.exports) { - module.exports = Mark; -} -else if (typeof define === "function" && define.amd) { - define(function() { - return Mark; - }); -}