Permalink
Browse files

Implements brackets tags and log messages. Version bump to 0.2.0

  • Loading branch information...
1 parent cc527d0 commit 9775706457acef555468aa5804fff821f6f9b1c8 @claudioc committed Feb 16, 2013
View
@@ -18,7 +18,7 @@ Features
- Show differences between document revisions
- Search through the content and the page names
- Layout accepts custom sidebar and footer
-- Can use custom css and JavaScript scripts
+- Can use custom CSS and JavaScript scripts
- White list for authorization on page reading and writing
- Detects unwritten pages (will appear in red)
- Automatically push to a remote
@@ -72,3 +72,24 @@ You can customize jingo in four different ways:
All those files are cached (thus, not re-read for every page load, but kept in memory). This means that for every modification in _style.css and _script.js you need to restart the server (sorry, working on that). This is not true for the footer and the sidebar but ONLY IF you edit those pages from jingo (which in that case will clear the cache by itself).
jingo uses twitter Bootstrap and jQuery as its front-end components.
+
+Editing
+-------
+
+To link to another Jingo wiki page, use the Jingo Page Link Tag.
+
+ [[Jingo Works]]
+
+The above tag will create a link to the corresponding page file named
+`jingo-works.md`. The conversion is as follows:
+
+ 1. Replace any spaces (U+0020) with dashes (U+002D)
+ 2. Replace any slashes (U+002F) with dashes (U+002D)
+
+If you'd like the link text to be something that doesn't map directly to the
+page name, you can specify the actual page name after a pipe:
+
+ [[How Jingo works|Jingo Works]]
+
+The above tag will link to `jingo-works.md` using "How Jingo Works" as the link text.
+
View
2 jingo
@@ -14,7 +14,7 @@ var express = require('express')
, yaml = require("yaml")
, program = require('commander');
-program.version('0.1.5')
+program.version('0.2.0')
.option('-c, --config <path>', 'Specify the config file')
.option('-s, --sample-config', 'Dumps a config file template and exits')
.parse(process.argv);
View
@@ -128,7 +128,7 @@ module.exports = Gitmech = (function() {
gitExec(["log", "-" + howMany, "--reverse", "--no-notes", "--pretty=format:%h%n%H%n%an%n%ae%n%aD%n%ar%n%at%n%s", version, "--", path], function(err, data) {
- var logdata = data.toString().split("\n")
+ var logdata = data ? data.toString().split("\n") : []
, group
, metadata = [];
View
@@ -11,6 +11,7 @@ var normalize = function(str) {
str = iconv.convert(str)
.toString()
.replace(/\s/g, '-')
+ .replace(/\//g, '-')
.replace(/[^a-zA-Z0-9\- _]/g, "")
.toLowerCase();
View
@@ -1,5 +1,7 @@
var Marked = require("marked");
+var Crypto = require('crypto');
+var Namer = require("../lib/namer");
Marked.setOptions({
gfm: true,
@@ -10,37 +12,67 @@ Marked.setOptions({
}
});
+var tagmap = {};
-var Renderer = {
-
- tagmap: {},
+// Yields the content with the rendered [[bracket tags]]
+// The rules are the same for Gollum https://github.com/github/gollum
+function extractTags(text) {
- render: function(content) {
-
- var text = Marked(content);
-
- return Marked(content);
- },
-
- // Yields the content with the rendered [[bracket tags]]
- // The rules are the same for Gollum https://github.com/github/gollum
- compileMarkup: function(content) {
-
- var text = content.match(/(.?)\[\[(.+?)\]\]([^\[]?)/g);
-console.log(text);
- return text;
+ tagmap = {};
+
+ var matches = text.match(/(.?)\[\[(.+?)\]\]([^\[]?)/g)
+ , tag
+ , id;
- },
+ // TODO text the ' at the beginning
+ if (matches) {
+ matches.forEach(function(match) {
+ tag = /\[\[(.+?)\]\]/.exec(match)[1];
+ id = Crypto.createHash('sha1').update(tag).digest("hex")
+ tagmap[id] = tag;
+ text = text.replace(tag, id);
+ });
- extractTags: function(content) {
+ }
+ return text;
+}
+
+function evalTags(text) {
+ var parts
+ , name
+ , pageName
+ , re;
+
+ for (var k in tagmap) {
+ parts = tagmap[k].split("|");
+ name = pageName = parts[0];
+ if (parts[1]) {
+ pageName = parts[1];
+ }
+ pageName = Namer.normalize(pageName);
+
+ tagmap[k] = "<a class=\"internal\" href=\"/wiki/" + pageName + "\">" + name + "</a>";
+ }
- return
+ for (k in tagmap) {
+ re = new RegExp("\\[\\[" + k + "\\]\\]", "g");
+ text = text.replace(re, tagmap[k]);
}
+ return text.replace(/\n/g, "");
+}
+
+var Renderer = {
+ render: function(content) {
+ var text = Marked(content);
+ text = extractTags(text);
+ text = evalTags(text);
+ return text;
+ }
};
View
@@ -1,6 +1,6 @@
{
"name": "jingo",
- "version": "0.1.5",
+ "version": "0.2.0",
"description": "A nodejs based wiki engine",
"author": "Claudio Cicali <claudio.cicali@gmail.com>",
"main": "jingo",
View
@@ -148,3 +148,18 @@ input#pageTitle {
a.absent {
color: red;
}
+
+form.edit {
+ border-radius: 4px;
+ background-color: #eee;
+ padding: 2%;
+ padding-bottom: 1%;
+ width: 96%;
+}
+
+form.edit input[type=text] {
+ width: 98%;
+}
+form.edit textarea {
+ width: 103%;
+}
View
@@ -26,22 +26,30 @@
return false;
});
- if (window.location.pathname.match(/^\/wiki\//)) {
- var pages = $.map($("#content a[href]").filter(function(i, a) {
- var href = $(a).attr("href");
- return !(href[0] == '/' || href.match(/^(f|ht)tps?:/));
- }), function(a) {
- return a.getAttribute("href").split("#")[0];
+ if (/^\/wiki\//.test(window.location.pathname)) {
+ var pages = []
+ , match
+ , href;
+
+ $("#content a.internal").each(function(i, a) {
+ href = $(a).attr("href");
+ if (match = /\/wiki\/(.+)/.exec(href)) {
+ pages.push(match[1]);
+ }
});
$.getJSON("/misc/existence", {data: pages}, function(result) {
$.each(result.data, function(href, a) {
- $("#content a[href=" + a + "]").addClass("absent");
+ $("#content a[href=\\/wiki\\/" + a + "]").addClass("absent");
});
});
}
function toggleCompareCheckboxes() {
+ if ($hCol1.find(":checkbox").length == 1) {
+ $hCol1.find(":checkbox").hide();
+ return;
+ }
if ($hCol1.find(":checked").length == 2) {
$hCol1.find(":not(:checked)")
.hide();

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -239,6 +239,7 @@ exports.pageUpdate = function(req, res) {
var pageName = res.locals.pageName = Namer.normalize(req.params.page)
, errors
, pageFile
+ , message
, content;
req.check('pageTitle', 'Page title cannot be empty').notEmpty();
@@ -255,12 +256,15 @@ exports.pageUpdate = function(req, res) {
req.sanitize('pageTitle').trim();
req.sanitize('content').trim();
+ req.sanitize('message').trim();
content = "#" + req.body.pageTitle + "\n" + req.body.content;
pageFile = app.locals.repo + "/" + pageName + ".md";
+ message = (req.body.message == "") ? "Content updated (" + pageName + ")" : req.body.message;
+
Fs.writeFile(pageFile, content, function() {
- app.locals.Git.add(pageName + ".md", "Content updated (" + pageName + ")", req.user.asGitAuthor, function(err) {
+ app.locals.Git.add(pageName + ".md", message, req.user.asGitAuthor, function(err) {
Locker.unlock(pageName);
if (pageName == '_footer') {
app.locals._footer = null;
@@ -418,7 +422,7 @@ exports.miscSyntaxReference = function(req, res) {
res.render('syntax');
}
-// Filters out pages that does not exist in the index
+// Filters out pages that do not exist in the index
exports.miscExistence = function(req, res) {
if (!req.query.data) {
View
@@ -14,7 +14,7 @@ describe ("Namer", function() {
expect(Namer.normalize("CoffeE")).to.equal("coffee");
expect(Namer.normalize("Caffé")).to.equal("caffe");
expect(Namer.normalize("Caffé corretto!")).to.equal("caffe-corretto");
- expect(Namer.normalize("Caff<p>e</p> senza schiuma")).to.equal("caffpep-senza-schiuma");
+ expect(Namer.normalize("Caff<p>e</p> senza schiuma")).to.equal("caffpe-p-senza-schiuma");
expect(Namer.normalize("Per favore: nessun, dico; E un punto...")).to.equal("per-favore-nessun-dico-e-un-punto");
});
View
@@ -5,11 +5,39 @@ var Renderer = require("../../lib/renderer");
describe ("Renderer", function() {
- it ("should render bracket tags", function() {
+ it ("should render bracket tags1", function() {
+ var text = "a [[Foo]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foo\">Foo</a> b</p>");
+ });
+
+ it ("should render bracket tags2", function() {
+ var text = "a [[Foo]][[Foo]][[Foo]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foo\">Foo</a><a class=\"internal\" href=\"/wiki/foo\">Foo</a><a class=\"internal\" href=\"/wiki/foo\">Foo</a> b</p>");
+ });
+
+ it ("should render bracket tags3", function() {
+ var text = "a [[Foo Bar]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foo-bar\">Foo Bar</a> b</p>");
+ });
+ it ("should render bracket tags4", function() {
var text = "a [[Foo]][[Bar]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foo\">Foo</a><a class=\"internal\" href=\"/wiki/bar\">Bar</a> b</p>");
+ });
+
+ it ("should render bracket tags5", function() {
+ var text = "a [[Foo]] [[Bar]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foo\">Foo</a> <a class=\"internal\" href=\"/wiki/bar\">Bar</a> b</p>");
+ });
+
+ it ("should render bracket tags6", function() {
+ var text = "a [[Il marito di Foo|Foobar]] [[Bar]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foobar\">Il marito di Foo</a> <a class=\"internal\" href=\"/wiki/bar\">Bar</a> b</p>");
+ });
- expect(Renderer.compileMarkup(text)).to.be.a("<p>a <a class=\"internal\" href=\"/Foo\">Foo</a><a class=\"internal\" href=\"/Bar\">Bar</a> b</p>");
+ it ("should render bracket tags7", function() {
+ var text = "a [[Foo / Bar]] b";
+ expect(Renderer.render(text)).to.be.equal("<p>a <a class=\"internal\" href=\"/wiki/foo---bar\">Foo / Bar</a> b</p>");
});
View
@@ -10,16 +10,16 @@ block content
mixin errors()
- form(action='/pages', method='post')
+ form(action='/pages', method='post', class='edit')
input(type="hidden", name="pageName", value="#{coalesce(pageName, '')}")
div
label Page title
- input(type='text', name='pageTitle', value="#{coalesce(formData.pageTitle, '')}")#pageTitle.span8
+ input(type='text', name='pageTitle', value="#{coalesce(formData.pageTitle, '')}")#pageTitle
div
- textarea(name="content", rows=25)#editor.span8 #{coalesce(formData.content, '')}
+ textarea(name="content", rows=25)#editor #{coalesce(formData.content, '')}
mixin saveAndCancel()
View
@@ -19,15 +19,18 @@ block content
mixin errors()
mixin warning()
- form(action='/pages/#{pageName}', method='post')
+ form(action='/pages/#{pageName}', method='post', class='edit')
div
label Page title
- input(type='text', name='pageTitle', value="#{coalesce(formData.pageTitle, '')}")#pageTitle.span8
+ input(type='text', name='pageTitle', value="#{coalesce(formData.pageTitle, '')}")#pageTitle
input(type="hidden", name="_method", value="put")
div
- textarea(name="content", rows=25)#editor.span8 #{coalesce(formData.content, '')}
+ textarea(name="content", rows=25)#editor #{coalesce(formData.content, '')}
+
+ div
+ input(type='text', name='message', placeholder='Write a small message here explaining this change (optional)')#message.span8
mixin saveAndCancel()
View
@@ -19,6 +19,7 @@ mixin markitupStylesheets()
link(rel="stylesheet", type="text/css", href="/vendor/markitup/sets/markdown/style.css")
mixin markitupJavaScripts()
+ script(src="/vendor/jquery-migrate-1.1.0.min.js")
script(src="/vendor/markitup/jquery.markitup.js")
script(src="/vendor/markitup/sets/markdown/set.js")
script

0 comments on commit 9775706

Please sign in to comment.