Compare commits

...

14 Commits

Author SHA1 Message Date
Aaron O'Mullan 6bce93d893 Bump version to 0.2.3 2014-04-18 11:53:18 -07:00
Aaron O'Mullan c9db068227 Refactor navigation generation to be functional
This makes it a lot less error prone and far more robust. As well as a
lot easier to understand
2014-04-18 11:49:55 -07:00
Samy Pessé 8a6d36ac4d Improve stability of mixpanel tracking 2014-04-18 20:42:18 +02:00
Samy Pessé 64622c5945 Load css before content 2014-04-18 20:19:45 +02:00
Samy Pessé 837813edb0 Add warning about theme 2014-04-18 20:16:21 +02:00
Aaron O'Mullan 0d9d62f1a9 Merge pull request #117 from GitbookIO/fix/99
Fix #99
2014-04-18 10:17:20 -07:00
Samy Pessé f7e9abf984 Fix quiz interractivity 2014-04-18 19:10:13 +02:00
Samy Pessé dd0579b60d Update tests for relative images 2014-04-18 17:23:33 +02:00
Samy Pessé b5bb953e47 Fix image path calcul 2014-04-18 17:15:03 +02:00
Samy Pessé ef4a8ea1b6 Fix #116: update url before updating html content 2014-04-18 15:49:53 +02:00
Samy Pessé 49cde96ee7 Add contributors to package.json and change homepage 2014-04-18 15:00:40 +02:00
Samy Pessé 627be20e48 Add editor and twitter links in the readme 2014-04-18 14:48:03 +02:00
Samy Pessé e45bb84b02 Fix navigation on chrome for iOS: disable pushState 2014-04-18 13:09:16 +02:00
Aaron O'Mullan 4e8876c725 Update README.md 2014-04-17 16:39:06 -07:00
14 changed files with 127 additions and 82 deletions
-4
View File
@@ -35,7 +35,6 @@ module.exports = function (grunt) {
"lodash": 'vendors/lodash', "lodash": 'vendors/lodash',
"requireLib": 'vendors/require', "requireLib": 'vendors/require',
"Mousetrap": 'vendors/mousetrap', "Mousetrap": 'vendors/mousetrap',
"mixpanel": 'vendors/mixpanel',
"lunr": path.join(__dirname, "node_modules/lunr/lunr") "lunr": path.join(__dirname, "node_modules/lunr/lunr")
}, },
shim: { shim: {
@@ -48,9 +47,6 @@ module.exports = function (grunt) {
'Mousetrap': { 'Mousetrap': {
exports: 'Mousetrap' exports: 'Mousetrap'
}, },
'mixpanel': {
exports: 'mixpanel'
},
'lunr': { 'lunr': {
exports: 'lunr' exports: 'lunr'
} }
+3 -1
View File
@@ -3,7 +3,7 @@ GitBook
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook) [![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/). GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/). An [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter.
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png) ![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@@ -39,6 +39,8 @@ Options for commands `build` and `serve` are:
--theme <path> Path to theme directory --theme <path> Path to theme directory
``` ```
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
## Output Formats ## Output Formats
GitBook can generate your book in the following formats: GitBook can generate your book in the following formats:
+30 -49
View File
@@ -6,6 +6,12 @@ function clean(obj) {
return obj && _.omit(obj, ['articles']); return obj && _.omit(obj, ['articles']);
} }
function flattenChapters(chapters) {
return _.reduce(chapters, function(accu, chapter) {
return accu.concat([clean(chapter)].concat(chapter.articles));
}, []);
}
// Returns from a summary a map of // Returns from a summary a map of
/* /*
{ {
@@ -20,62 +26,37 @@ function navigation(summary, files) {
// Support single files as well as list // Support single files as well as list
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null); files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
// Mapping of prev/next for a give path
var mapping = {};
// Special README nav // Special README nav
var README_NAV = { var README_NAV = {
path: 'README.md', path: 'README.md',
title: 'Introduction', title: 'Introduction',
};
// Walk the chapter/article tree and create navigation entires
_.each(summary.chapters, function(chapter, idx, chapters) {
// Skip if no path
if(!chapter.path) return;
var currentChapter = clean(chapter);
var prevChapter = (idx-1 < 0) ? README_NAV : chapters[idx-1];
var nextChapter = (idx+1 >= chapters.length) ? null : chapters[idx+1];
var prev = (!prevChapter || _.isEmpty(prevChapter.articles)) ?
prevChapter : _.last(prevChapter.articles);
var next = (!chapter || _.isEmpty(chapter.articles)) ?
nextChapter : _.first(chapter.articles);
// Add chapter mapping
mapping[chapter.path] = {
title: chapter.title,
prev: clean(prev),
next: clean(next),
level: (idx+1).toString(),
};
// Check a chapter's articles
_.each(chapter.articles, function(article, _idx, articles) {
// Skip if no path
if(!article.path) return;
var prev = (_idx-1 < 0) ? currentChapter : clean(articles[_idx-1]);
var next = (_idx+1 >= articles.length) ? nextChapter : clean(articles[_idx+1]);
mapping[article.path] = {
title: article.title,
prev: clean(prev),
next: clean(next),
level: [idx+1, _idx+1].join('.'),
};
});
});
// Hack for README.html
mapping['README.md'] = {
title: README_NAV.title,
prev: null,
next: clean(summary.chapters[0]),
level: '0', level: '0',
}; };
// List of all navNodes
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
var prevNodes = [null].concat(navNodes.slice(0, -1));
var nextNodes = navNodes.slice(1).concat([null]);
// Mapping of prev/next for a give path
var mapping = _.chain(_.zip(navNodes, prevNodes, nextNodes))
.map(function(nodes) {
var current = nodes[0], prev = nodes[1], next = nodes[2];
// Skip if no path
if(!current.path) return null;
return [current.path, {
title: current.title,
prev: prev,
next: next,
level: current.level,
}];
})
.filter()
.object()
.value();
// Filter for only files we want // Filter for only files we want
if(files) { if(files) {
return _.pick(mapping, files); return _.pick(mapping, files);
+8 -2
View File
@@ -79,8 +79,14 @@ GitBookRenderer.prototype.image = function(href, title, text) {
// Relative image, rewrite it depending output // Relative image, rewrite it depending output
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) { if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) {
var outdir = o.outdir.charAt(o.outdir.length - 1) === '/' ? o.outdir : o.outdir + '/'; // o.dir: directory parent of the file currently in rendering process
_href = url.resolve(outdir, [o.dir, href].join('/')); // o.outdir: directory parent from the html output
// Absolute file in source
_href = path.join(o.dir, _href);
// make it relative to output
_href = path.relative(o.outdir, _href);
} }
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text); return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
+14 -3
View File
@@ -1,6 +1,7 @@
{ {
"name": "gitbook", "name": "gitbook",
"version": "0.2.2", "version": "0.2.3",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks", "description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
@@ -39,9 +40,19 @@
"book", "book",
"gitbook" "gitbook"
], ],
"author": "Aaron O'Mullan <aaron.omullan@friendco.de>", "author": "FriendCode <contact@friendco.de>",
"license": "Apache 2", "license": "Apache 2",
"bugs": { "bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues" "url": "https://github.com/GitbookIO/gitbook/issues"
} },
"contributors": [
{
"name": "Aaron O'Mullan",
"email": "aaron.omullan@gmail.com"
},
{
"name": "Samy Pessé",
"email": "samypesse@gmail.com"
}
]
} }
+12
View File
@@ -0,0 +1,12 @@
# Images Test
This is a test for images path calculation. It supposed this fiel is in a syntax/ folder
### #
[![Screen](./preview.png)](./preview.png)
### 2
[![Screen](../preview2.png)](./preview2.png)
+30 -4
View File
@@ -36,10 +36,6 @@ describe('Page parsing', function() {
assert(LEXED[2].content); assert(LEXED[2].content);
}); });
it('should make image URLs relative', function() {
assert(LEXED[2].content.indexOf('_book/assets/my-pretty-picture.png') !== -1);
});
it('should gen code and content for exercise sections', function() { it('should gen code and content for exercise sections', function() {
assert(LEXED[1].content); assert(LEXED[1].content);
assert(LEXED[1].code); assert(LEXED[1].code);
@@ -94,3 +90,33 @@ describe('Relative links', function() {
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1); assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
}); });
}); });
describe('Relative images', function() {
it('should keep image relative with considering output directory in site format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: 'syntax'
});
assert(LEXED[0].content.indexOf('"preview.png"') !== -1);
assert(LEXED[0].content.indexOf('"../preview2.png"') !== -1);
});
it('should keep image relative with considering output directory in page format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: './'
});
assert(LEXED[0].content.indexOf('"syntax/preview.png"') !== -1);
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
});
});
+2
View File
@@ -3,3 +3,5 @@
For creating a theme for GitBook, just copy this folder. The theme folder should at least contains two folders: `assets` and `templates`. For creating a theme for GitBook, just copy this folder. The theme folder should at least contains two folders: `assets` and `templates`.
The theme can be changed using the option ```--theme=<path>``` The theme can be changed using the option ```--theme=<path>```
#### Warning! Theme system is going to change before version 1.0.0
+1 -1
View File
File diff suppressed because one or more lines are too long
+5 -3
View File
@@ -10,12 +10,14 @@ define([
var prev, next; var prev, next;
var githubCountStars, githubCountWatch; var githubCountStars, githubCountWatch;
var usePushState = !navigator.userAgent.match('CriOS') && (typeof history.pushState !== "undefined");
var updateHistory = function(url, title) { var updateHistory = function(url, title) {
history.pushState({ path: url }, title, url); history.pushState({ path: url }, title, url);
}; };
var handleNavigation = function(url, push) { var handleNavigation = function(url, push) {
if (typeof history.pushState === "undefined") { if (!usePushState) {
// Refresh the page to the new URL if pushState not supported // Refresh the page to the new URL if pushState not supported
location.href = url; location.href = url;
return return
@@ -23,6 +25,8 @@ define([
return $.get(url) return $.get(url)
.done(function (html) { .done(function (html) {
if (push) updateHistory(url, null);
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){ html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>'; return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
}); });
@@ -49,8 +53,6 @@ define([
// Update state // Update state
state.update($("html")); state.update($("html"));
if (push) updateHistory(url, null);
preparePage(); preparePage();
}) })
.fail(function () { .fail(function () {
+5 -2
View File
@@ -20,8 +20,11 @@ define([
$quiz.find(".question").each(function(q) { $quiz.find(".question").each(function(q) {
var result = true; var result = true;
var $answers = $quiz.find(".question-answers").slice(q).find("input[type=radio], input[type=checkbox]");
$(this).find("input[type=radio],input[type=checkbox]").each(function(i) { var $questions = $(this).find(".question-content").find("input[type=radio], input[type=checkbox]");
var $answers = $(this).find(".question-answers").find("input[type=radio], input[type=checkbox]");
$questions.each(function(i) {
var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked"); var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked");
result = result && correct; result = result && correct;
if (!correct) { if (!correct) {
+13 -10
View File
@@ -1,13 +1,6 @@
define([ define([
"lodash", "lodash"
"mixpanel" ], function(_) {
], function(_, mixpanel) {
mixpanel.init("01eb2b950ae09a5fdb15a98dcc5ff20e", {
loaded: function() {
track("page.start");
}
});
var isAvailable = function() { var isAvailable = function() {
return ( return (
typeof mixpanel !== "undefined" && typeof mixpanel !== "undefined" &&
@@ -15,9 +8,14 @@ define([
); );
}; };
var track = function(e, data) { var track = function(e, data, t) {
if (!isAvailable()) { if (!isAvailable()) {
console.warn("tracking not available!"); console.warn("tracking not available!");
t = t || 500;
setTimeout(function() {
console.log(" -> retest tracking");
track(e, data, t*2);
}, t);
return; return;
} }
console.log("track", e); console.log("track", e);
@@ -26,6 +24,11 @@ define([
})); }));
}; };
setTimeout(function() {
mixpanel.init("01eb2b950ae09a5fdb15a98dcc5ff20e");
track("page.start");
}, 0);
return { return {
isAvailable: isAvailable, isAvailable: isAvailable,
track: track track: track
-2
View File
@@ -1,2 +0,0 @@
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
+4 -1
View File
@@ -35,11 +35,14 @@
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
{% block content %}{% endblock %}
{% block style %} {% block style %}
<link rel="stylesheet" href="{{ staticBase }}/style.css"> <link rel="stylesheet" href="{{ staticBase }}/style.css">
{% endblock %} {% endblock %}
{% block content %}{% endblock %}
{% block javascript %} {% block javascript %}
<script type="text/javascript">(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>
<script src="{{ staticBase }}/jsrepl/jsrepl.js" id="jsrepl-script"></script> <script src="{{ staticBase }}/jsrepl/jsrepl.js" id="jsrepl-script"></script>