diff --git a/lib/GUI/css/main.less b/lib/GUI/css/main.less
index c5d2892bc..c5f1e1381 100644
--- a/lib/GUI/css/main.less
+++ b/lib/GUI/css/main.less
@@ -1,4 +1,5 @@
@import "../../../node_modules/helpers.less/helpers.less";
+@import "./markdown.css";
/*** Main Styles ***/
body {
@@ -51,8 +52,8 @@ h1 {
/*** Package Entries ***/
.entry {
background: #EEE;
- .border-radius(3px);
- padding: 15px;
+ .border-radius(4px);
+ padding: 12px 15px;
h3 {
font-size: 24px;
diff --git a/lib/GUI/css/markdown.css b/lib/GUI/css/markdown.css
new file mode 100644
index 000000000..d45fe4472
--- /dev/null
+++ b/lib/GUI/css/markdown.css
@@ -0,0 +1,279 @@
+/*** Sourced from this Gist: https://gist.github.com/andyferra/2554919 ***/
+
+body {
+ font-family: Helvetica, arial, sans-serif;
+ font-size: 14px;
+ line-height: 1.6;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ background-color: white;
+ padding: 30px; }
+
+body > *:first-child {
+ margin-top: 0 !important; }
+body > *:last-child {
+ margin-bottom: 0 !important; }
+
+a {
+ color: #4183C4; }
+a.absent {
+ color: #cc0000; }
+a.anchor {
+ display: block;
+ padding-left: 30px;
+ margin-left: -30px;
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0; }
+
+h1, h2, h3, h4, h5, h6 {
+ margin: 20px 0 10px;
+ padding: 0;
+ font-weight: bold;
+ -webkit-font-smoothing: antialiased;
+ cursor: text;
+ position: relative; }
+
+h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
+ background: url("../../images/modules/styleguide/para.png") no-repeat 10px center;
+ text-decoration: none; }
+
+h1 tt, h1 code {
+ font-size: inherit; }
+
+h2 tt, h2 code {
+ font-size: inherit; }
+
+h3 tt, h3 code {
+ font-size: inherit; }
+
+h4 tt, h4 code {
+ font-size: inherit; }
+
+h5 tt, h5 code {
+ font-size: inherit; }
+
+h6 tt, h6 code {
+ font-size: inherit; }
+
+h1 {
+ font-size: 28px;
+ color: black; }
+
+h2 {
+ font-size: 24px;
+ border-bottom: 1px solid #cccccc;
+ color: black; }
+
+h3 {
+ font-size: 18px; }
+
+h4 {
+ font-size: 16px; }
+
+h5 {
+ font-size: 14px; }
+
+h6 {
+ color: #777777;
+ font-size: 14px; }
+
+p, blockquote, ul, ol, dl, li, table, pre {
+ margin: 15px 0; }
+
+hr {
+ background: transparent url("../../images/modules/pulls/dirty-shade.png") repeat-x 0 0;
+ border: 0 none;
+ color: #cccccc;
+ height: 4px;
+ padding: 0; }
+
+body > h2:first-child {
+ margin-top: 0;
+ padding-top: 0; }
+body > h1:first-child {
+ margin-top: 0;
+ padding-top: 0; }
+ body > h1:first-child + h2 {
+ margin-top: 0;
+ padding-top: 0; }
+body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
+ margin-top: 0;
+ padding-top: 0; }
+
+a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
+ margin-top: 0;
+ padding-top: 0; }
+
+h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
+ margin-top: 0; }
+
+li p.first {
+ display: inline-block; }
+
+ul, ol {
+ padding-left: 30px; }
+
+ul :first-child, ol :first-child {
+ margin-top: 0; }
+
+ul :last-child, ol :last-child {
+ margin-bottom: 0; }
+
+dl {
+ padding: 0; }
+ dl dt {
+ font-size: 14px;
+ font-weight: bold;
+ font-style: italic;
+ padding: 0;
+ margin: 15px 0 5px; }
+ dl dt:first-child {
+ padding: 0; }
+ dl dt > :first-child {
+ margin-top: 0; }
+ dl dt > :last-child {
+ margin-bottom: 0; }
+ dl dd {
+ margin: 0 0 15px;
+ padding: 0 15px; }
+ dl dd > :first-child {
+ margin-top: 0; }
+ dl dd > :last-child {
+ margin-bottom: 0; }
+
+blockquote {
+ border-left: 4px solid #dddddd;
+ padding: 0 15px;
+ color: #777777; }
+ blockquote > :first-child {
+ margin-top: 0; }
+ blockquote > :last-child {
+ margin-bottom: 0; }
+
+table {
+ padding: 0; }
+ table tr {
+ border-top: 1px solid #cccccc;
+ background-color: white;
+ margin: 0;
+ padding: 0; }
+ table tr:nth-child(2n) {
+ background-color: #f8f8f8; }
+ table tr th {
+ font-weight: bold;
+ border: 1px solid #cccccc;
+ text-align: left;
+ margin: 0;
+ padding: 6px 13px; }
+ table tr td {
+ border: 1px solid #cccccc;
+ text-align: left;
+ margin: 0;
+ padding: 6px 13px; }
+ table tr th :first-child, table tr td :first-child {
+ margin-top: 0; }
+ table tr th :last-child, table tr td :last-child {
+ margin-bottom: 0; }
+
+img {
+ max-width: 100%; }
+
+span.frame {
+ display: block;
+ overflow: hidden; }
+ span.frame > span {
+ border: 1px solid #dddddd;
+ display: block;
+ float: left;
+ overflow: hidden;
+ margin: 13px 0 0;
+ padding: 7px;
+ width: auto; }
+ span.frame span img {
+ display: block;
+ float: left; }
+ span.frame span span {
+ clear: both;
+ color: #333333;
+ display: block;
+ padding: 5px 0 0; }
+span.align-center {
+ display: block;
+ overflow: hidden;
+ clear: both; }
+ span.align-center > span {
+ display: block;
+ overflow: hidden;
+ margin: 13px auto 0;
+ text-align: center; }
+ span.align-center span img {
+ margin: 0 auto;
+ text-align: center; }
+span.align-right {
+ display: block;
+ overflow: hidden;
+ clear: both; }
+ span.align-right > span {
+ display: block;
+ overflow: hidden;
+ margin: 13px 0 0;
+ text-align: right; }
+ span.align-right span img {
+ margin: 0;
+ text-align: right; }
+span.float-left {
+ display: block;
+ margin-right: 13px;
+ overflow: hidden;
+ float: left; }
+ span.float-left span {
+ margin: 13px 0 0; }
+span.float-right {
+ display: block;
+ margin-left: 13px;
+ overflow: hidden;
+ float: right; }
+ span.float-right > span {
+ display: block;
+ overflow: hidden;
+ margin: 13px auto 0;
+ text-align: right; }
+
+code, tt {
+ margin: 0 2px;
+ padding: 0 5px;
+ white-space: nowrap;
+ border: 1px solid #eaeaea;
+ background-color: #f8f8f8;
+ border-radius: 3px; }
+
+pre code {
+ margin: 0;
+ padding: 0;
+ white-space: pre;
+ border: none;
+ background: transparent; }
+
+.highlight pre {
+ background-color: #f8f8f8;
+ border: 1px solid #cccccc;
+ font-size: 13px;
+ line-height: 19px;
+ overflow: auto;
+ padding: 6px 10px;
+ border-radius: 3px; }
+
+pre {
+ background-color: #f8f8f8;
+ border: 1px solid #cccccc;
+ font-size: 13px;
+ line-height: 19px;
+ overflow: auto;
+ padding: 6px 10px;
+ border-radius: 3px; }
+ pre code, pre tt {
+ background-color: transparent;
+ border: none; }
\ No newline at end of file
diff --git a/lib/GUI/js/entry.js b/lib/GUI/js/entry.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/lib/GUI/js/main.js b/lib/GUI/js/main.js
index 0e35c1461..c87595be0 100644
--- a/lib/GUI/js/main.js
+++ b/lib/GUI/js/main.js
@@ -1,49 +1,2 @@
-var $ = require('unopinionate').selector,
- template = require('../entry.handlebars');
-
-$(function() {
- var $form = $('#search-form'),
- $input = $form.find('input'),
- $searchResults = $("#search-results"),
- $body = $('body'),
- request;
-
- $form.bind('submit keyup', function(e) {
- e.preventDefault();
-
- var q = $input.val();
-
- $body.addClass('state-search');
-
- if(q) {
- if(request) {
- request.abort();
- }
-
- request = $.getJSON('/-/search/' + q, function(results) {
- if(results.length) {
- var html = '';
-
- $.each(results, function(i, package) {
- html += template(package);
- });
-
- $searchResults.html(html);
- }
- else {
- $searchResults.html("
No Results
");
- }
- });
- }
- else {
- $searchResults.html('');
- $body.removeClass('state-search');
- }
- });
-
- $form.find('.clear').click(function(e) {
- e.preventDefault();
- $input.val('');
- $form.keyup();
- });
-});
+require('./search');
+require('./entry');
diff --git a/lib/GUI/js/search.js b/lib/GUI/js/search.js
new file mode 100644
index 000000000..0e35c1461
--- /dev/null
+++ b/lib/GUI/js/search.js
@@ -0,0 +1,49 @@
+var $ = require('unopinionate').selector,
+ template = require('../entry.handlebars');
+
+$(function() {
+ var $form = $('#search-form'),
+ $input = $form.find('input'),
+ $searchResults = $("#search-results"),
+ $body = $('body'),
+ request;
+
+ $form.bind('submit keyup', function(e) {
+ e.preventDefault();
+
+ var q = $input.val();
+
+ $body.addClass('state-search');
+
+ if(q) {
+ if(request) {
+ request.abort();
+ }
+
+ request = $.getJSON('/-/search/' + q, function(results) {
+ if(results.length) {
+ var html = '';
+
+ $.each(results, function(i, package) {
+ html += template(package);
+ });
+
+ $searchResults.html(html);
+ }
+ else {
+ $searchResults.html("No Results
");
+ }
+ });
+ }
+ else {
+ $searchResults.html('');
+ $body.removeClass('state-search');
+ }
+ });
+
+ $form.find('.clear').click(function(e) {
+ e.preventDefault();
+ $input.val('');
+ $form.keyup();
+ });
+});
diff --git a/lib/index.js b/lib/index.js
index 7bf9abda5..ba2ac2ef8 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -15,7 +15,8 @@ var express = require('express')
, fs = require('fs')
, localList = require('./local-list')
, Search = require('./search')
- , _ = require('underscore');
+ , _ = require('underscore')
+ , marked = require('marked');
function match(regexp) {
return function(req, res, next, value, name) {
@@ -266,6 +267,19 @@ module.exports = function(config_hash) {
}
});
+ // Readme
+ marked.setOptions({
+ highlight: function (code) {
+ return require('highlight.js').highlightAuto(code).value;
+ }
+ });
+
+ app.get('/-/readme/:name/:version', function(req, res, next) {
+ storage.get_readme(req.params.name, req.params.version, function(readme) {
+ res.send(marked(readme));
+ });
+ });
+
// tagging a package
app.put('/:package/:tag', can('publish'), media('application/json'), function(req, res, next) {
if (typeof(req.body) !== 'string') return next('route')
diff --git a/lib/local-storage.js b/lib/local-storage.js
index 4fc0af5c5..a6eb4c814 100644
--- a/lib/local-storage.js
+++ b/lib/local-storage.js
@@ -8,7 +8,8 @@ var fs = require('fs')
, mystreams = require('./streams')
, Logger = require('./logger')
, info_file = 'package.json'
- , localList = require('./local-list');
+ , localList = require('./local-list')
+ , targz = require('tar.gz');
//
// Implements Storage interface
@@ -383,6 +384,41 @@ Storage.prototype.add_tarball = function(name, filename) {
return stream
}
+Storage.prototype.unpack_tarball = function(file, callback) {
+ new targz().extract(file + '.tgz', file, callback);
+};
+
+Storage.prototype.get_readme = function(name, version, callback) {
+ var self = this,
+ fileName = this.storage(name).path + '/' + name + '-' + version;
+
+ fs.exists(fileName, function(exists) {
+ if(exists) {
+ returnReadme();
+ }
+ else {
+ self.unpack_tarball(fileName, function(err) {
+ returnReadme();
+ });
+ }
+ });
+
+ function returnReadme() {
+ var readmeFileName = fileName + '/package/README.md';
+
+ fs.exists(readmeFileName, function(exists) {
+ if(exists) {
+ fs.readFile(readmeFileName, {encoding: "UTF-8"}, function(err, file) {
+ callback(file);
+ });
+ }
+ else {
+ callback('');
+ }
+ });
+ }
+};
+
Storage.prototype.get_tarball = function(name, filename, callback) {
assert(utils.validate_name(filename))
diff --git a/lib/static/main.css b/lib/static/main.css
index f0b7367f9..f95002c79 100644
--- a/lib/static/main.css
+++ b/lib/static/main.css
@@ -1,3 +1,4 @@
+@import "markdown.css";
/*** Main Styles ***/
body {
margin: 0;
@@ -43,10 +44,10 @@ h1 a:hover {
/*** Package Entries ***/
.entry {
background: #EEE;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
- padding: 15px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ padding: 12px 15px;
}
.entry h3 {
font-size: 24px;
@@ -68,4 +69,4 @@ h1 a:hover {
.state-search #all-packages {
display: none;
}
-/*# sourceMappingURL=data:application/json,%7B%22version%22%3A3%2C%22sources%22%3A%5B%22lib%2FGUI%2Fcss%2Fmain.less%22%2C%22node_modules%2Fhelpers.less%2Fhelpers.less%22%5D%2C%22names%22%3A%5B%5D%2C%22mappings%22%3A%22%3BAAGA%3BEACC%2CSAAA%3BEACA%2CaAAa%2CiBAAiB%2C8CAA9B%3B%3BAAGD%3BEACC%2CkBAAA%3B%3BAADD%2CEAGC%3BAAHD%2CEAGI%2CEAAC%3BEACH%2CYAAA%3BEACA%2CqBAAA%3B%3BAALF%2CEAQC%2CEAAC%3BEACA%2C0BAAA%3B%3BAAIF%3BEACC%2CkBAAA%3B%3BAAGD%3BEACC%2CcAAA%3BEACA%2CgBAAA%3BEACA%2CaAAA%3B%3B%3BAAID%3BEACC%2CmBAAA%3BEACA%2CkBAAA%3BEACA%2CqBAAA%3BEClBG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDkBH%2CgBAAA%3BEACA%2CWAAA%3B%3BAAND%2CMAQC%3BEACC%2CwCAAA%3B%3B%3BAAKF%3BEACC%2CYAAA%3B%3B%3BAAID%3BEACC%2CgBAAA%3BECnCG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDmCH%2CaAAA%3B%3BAAHD%2CMAKC%3BEACC%2CeAAA%3BEACA%2CgBAAA%3B%3BAAPF%2CMAUC%3BEACC%2CeAAA%3BEACA%2CWAAA%3B%3BAAZF%2CMAeC%3BEACC%2CeAAA%3BEACA%2CYAAA%3BEACA%2CWAAA%3B%3BAAlBF%2CMAqBC%3BEACC%2CSAAA%3B%3B%3BAAKF%2CaAAc%3BEACb%2CaAAA%22%7D */
\ No newline at end of file
+/*# sourceMappingURL=data:application/json,%7B%22version%22%3A3%2C%22sources%22%3A%5B%22lib%2FGUI%2Fcss%2Fmain.less%22%2C%22node_modules%2Fhelpers.less%2Fhelpers.less%22%5D%2C%22names%22%3A%5B%5D%2C%22mappings%22%3A%22QACQ%3B%3BAAGR%3BEACC%2CSAAA%3BEACA%2CaAAa%2CiBAAiB%2C8CAA9B%3B%3BAAGD%3BEACC%2CkBAAA%3B%3BAADD%2CEAGC%3BAAHD%2CEAGI%2CEAAC%3BEACH%2CYAAA%3BEACA%2CqBAAA%3B%3BAALF%2CEAQC%2CEAAC%3BEACA%2C0BAAA%3B%3BAAIF%3BEACC%2CkBAAA%3B%3BAAGD%3BEACC%2CcAAA%3BEACA%2CgBAAA%3BEACA%2CaAAA%3B%3B%3BAAID%3BEACC%2CmBAAA%3BEACA%2CkBAAA%3BEACA%2CqBAAA%3BECnBG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDmBH%2CgBAAA%3BEACA%2CWAAA%3B%3BAAND%2CMAQC%3BEACC%2CwCAAA%3B%3B%3BAAKF%3BEACC%2CYAAA%3B%3B%3BAAID%3BEACC%2CgBAAA%3BECpCG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDoCH%2CkBAAA%3B%3BAAHD%2CMAKC%3BEACC%2CeAAA%3BEACA%2CgBAAA%3B%3BAAPF%2CMAUC%3BEACC%2CeAAA%3BEACA%2CWAAA%3B%3BAAZF%2CMAeC%3BEACC%2CeAAA%3BEACA%2CYAAA%3BEACA%2CWAAA%3B%3BAAlBF%2CMAqBC%3BEACC%2CSAAA%3B%3B%3BAAKF%2CaAAc%3BEACb%2CaAAA%22%7D */
\ No newline at end of file
diff --git a/lib/static/main.js b/lib/static/main.js
index 1a8a61253..9e4f8d1a2 100644
--- a/lib/static/main.js
+++ b/lib/static/main.js
@@ -22,7 +22,13 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+ "\n";
return buffer;
});
-},{"handlebars/runtime":9}],2:[function(require,module,exports){
+},{"handlebars/runtime":11}],2:[function(require,module,exports){
+
+},{}],3:[function(require,module,exports){
+require('./search');
+require('./entry');
+
+},{"./entry":2,"./search":4}],4:[function(require,module,exports){
var $ = require('unopinionate').selector,
template = require('../entry.handlebars');
@@ -73,7 +79,7 @@ $(function() {
});
});
-},{"../entry.handlebars":1,"unopinionate":10}],3:[function(require,module,exports){
+},{"../entry.handlebars":1,"unopinionate":12}],5:[function(require,module,exports){
"use strict";
/*globals Handlebars: true */
var base = require("./handlebars/base");
@@ -106,7 +112,7 @@ var Handlebars = create();
Handlebars.create = create;
exports["default"] = Handlebars;
-},{"./handlebars/base":4,"./handlebars/exception":5,"./handlebars/runtime":6,"./handlebars/safe-string":7,"./handlebars/utils":8}],4:[function(require,module,exports){
+},{"./handlebars/base":6,"./handlebars/exception":7,"./handlebars/runtime":8,"./handlebars/safe-string":9,"./handlebars/utils":10}],6:[function(require,module,exports){
"use strict";
var Utils = require("./utils");
var Exception = require("./exception")["default"];
@@ -287,7 +293,7 @@ exports.log = log;var createFrame = function(object) {
return obj;
};
exports.createFrame = createFrame;
-},{"./exception":5,"./utils":8}],5:[function(require,module,exports){
+},{"./exception":7,"./utils":10}],7:[function(require,module,exports){
"use strict";
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
@@ -316,7 +322,7 @@ function Exception(message, node) {
Exception.prototype = new Error();
exports["default"] = Exception;
-},{}],6:[function(require,module,exports){
+},{}],8:[function(require,module,exports){
"use strict";
var Utils = require("./utils");
var Exception = require("./exception")["default"];
@@ -454,7 +460,7 @@ exports.program = program;function invokePartial(partial, name, context, helpers
exports.invokePartial = invokePartial;function noop() { return ""; }
exports.noop = noop;
-},{"./base":4,"./exception":5,"./utils":8}],7:[function(require,module,exports){
+},{"./base":6,"./exception":7,"./utils":10}],9:[function(require,module,exports){
"use strict";
// Build out our basic SafeString type
function SafeString(string) {
@@ -466,7 +472,7 @@ SafeString.prototype.toString = function() {
};
exports["default"] = SafeString;
-},{}],8:[function(require,module,exports){
+},{}],10:[function(require,module,exports){
"use strict";
/*jshint -W004 */
var SafeString = require("./safe-string")["default"];
@@ -543,12 +549,12 @@ exports.escapeExpression = escapeExpression;function isEmpty(value) {
}
exports.isEmpty = isEmpty;
-},{"./safe-string":7}],9:[function(require,module,exports){
+},{"./safe-string":9}],11:[function(require,module,exports){
// Create a simple path alias to allow browserify to resolve
// the runtime on a supported path.
module.exports = require('./dist/cjs/handlebars.runtime');
-},{"./dist/cjs/handlebars.runtime":3}],10:[function(require,module,exports){
+},{"./dist/cjs/handlebars.runtime":5}],12:[function(require,module,exports){
(function (global){
(function(root) {
var unopinionate = {
@@ -575,4 +581,4 @@ module.exports = require('./dist/cjs/handlebars.runtime');
})(typeof window != 'undefined' ? window : global);
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{}]},{},[2])
\ No newline at end of file
+},{}]},{},[3])
\ No newline at end of file
diff --git a/lib/storage.js b/lib/storage.js
index dd238ac2f..17e3bc351 100644
--- a/lib/storage.js
+++ b/lib/storage.js
@@ -174,6 +174,10 @@ Storage.prototype.add_tarball = function(name, filename) {
return this.local.add_tarball(name, filename)
}
+Storage.prototype.get_readme = function(name, version, callback) {
+ return this.local.get_readme(name, version, callback);
+};
+
//
// Get a tarball from a storage for {name} package
//
diff --git a/package.json b/package.json
index 049fdde0f..6cab6def1 100644
--- a/package.json
+++ b/package.json
@@ -22,12 +22,15 @@
"express": "3.4.x",
"handlebars": "1.x.x",
"helpers.less": "git://github.com/bpeacock/helpers.less.git",
+ "highlight.js": "^8.0.0",
"js-yaml": ">= 3.0.1",
"lunr": "^0.5.2",
+ "marked": "^0.3.2",
"minimatch": ">= 0.2.14",
"mkdirp": ">= 0.3.5",
"request": ">= 2.31.0",
"semver": ">= 2.2.1",
+ "tar.gz": "^0.1.1",
"underscore": "^1.6.0",
"unopinionate": "0.0.4"
},