| @ -0,0 +1 @@ | |||
| node_modules | |||
| @ -0,0 +1,4 @@ | |||
| brett.is | |||
| ======== | |||
| Source code for the blog [brett.is](http://brett.is) | |||
| @ -0,0 +1,27 @@ | |||
| { | |||
| "locals": { | |||
| "url": "http://brett.is", | |||
| "name": "Brett Langdon's Blog", | |||
| "owner": "Brett Langdon", | |||
| "description": "" | |||
| }, | |||
| "plugins": [ | |||
| "./plugins/paginator.coffee" | |||
| ], | |||
| "require": { | |||
| "moment": "moment", | |||
| "_": "underscore", | |||
| "typogr": "typogr" | |||
| }, | |||
| "jade": { | |||
| "pretty": true | |||
| }, | |||
| "markdown": { | |||
| "smartLists": true, | |||
| "smartypants": true, | |||
| "gfm": true | |||
| }, | |||
| "paginator": { | |||
| "perPage": 3 | |||
| } | |||
| } | |||
| @ -0,0 +1,3 @@ | |||
| --- | |||
| view: none | |||
| --- | |||
| @ -0,0 +1,3 @@ | |||
| { | |||
| "template": "archive.jade" | |||
| } | |||
| @ -0,0 +1,5 @@ | |||
| { | |||
| "name": "Brett Langdon", | |||
| "email": "brett@blangdon.com", | |||
| "bio": "" | |||
| } | |||
| @ -0,0 +1,437 @@ | |||
| h1, h2, h3, h4, h5, h6, p, body, a, img, ul, ol, blockquote, pre { | |||
| margin: 0; padding: 0; border: 0; | |||
| } | |||
| body { | |||
| font-family: 'Lora', serif; | |||
| font-size: 21px; | |||
| line-height: 1.52; | |||
| background-color: #f8f8f8; | |||
| text-rendering: optimizeLegibility; | |||
| } | |||
| .content-wrap { | |||
| width: 34em; | |||
| margin: 0 auto; | |||
| } | |||
| body, a { | |||
| color: #171717; | |||
| } | |||
| a:hover { | |||
| color: #ff8000; | |||
| text-decoration: underline; | |||
| } | |||
| p { | |||
| margin-bottom: 1.52em; | |||
| } | |||
| pre { | |||
| font-size: 0.9em; | |||
| overflow: auto; | |||
| background: #fff; | |||
| border: 1px dashed #d2d2d2; | |||
| border-radius: 0.25em; | |||
| margin-bottom: 1.8em; | |||
| padding: 1em; | |||
| } | |||
| h1 { | |||
| font-size: 2em; | |||
| margin-bottom: 1em; | |||
| } | |||
| h2 { | |||
| font-size: 1.2em; | |||
| font-weight: 400; | |||
| line-height: 1.43; | |||
| margin-bottom: 1.35em; | |||
| } | |||
| h3 { | |||
| font-style: italic; | |||
| text-align: center; | |||
| font-weight: 400; | |||
| font-size: 1.4em; | |||
| margin-top: 1.8em; | |||
| margin-bottom: 0.8em; | |||
| } | |||
| ol, ul { | |||
| margin: 0 1.4em 1.4em 4em; | |||
| } | |||
| li { | |||
| margin-bottom: 0.5em; | |||
| } | |||
| blockquote { | |||
| margin: 1.2em 3em; | |||
| padding-left: 1em; | |||
| font-style: italic; | |||
| } | |||
| hr { | |||
| border: 0; | |||
| border-top: 1px dashed #d2d2d2; | |||
| height: 0; | |||
| margin: 1.6em 0; | |||
| } | |||
| /* page header */ | |||
| .header { | |||
| margin: 3em 0 5em; | |||
| } | |||
| .header h1 { | |||
| font-size: 2.6em; | |||
| text-align: center; | |||
| font-weight: 700; | |||
| margin: 0; | |||
| } | |||
| .header a, .header a:hover { | |||
| text-decoration: none; | |||
| color: #171717; | |||
| } | |||
| .header .author { | |||
| font-family: 'Merriweather', serif; | |||
| font-variant: small-caps; | |||
| text-transform: lowercase; | |||
| text-rendering: auto; | |||
| text-align: center; | |||
| font-weight: 400; | |||
| letter-spacing: 1px; | |||
| } | |||
| .header .description { | |||
| font-size: 1.2em; | |||
| font-style: italic; | |||
| text-align: center; | |||
| margin-top: -0.3em; | |||
| } | |||
| body.article-detail > header h1 { | |||
| font-size: 2.5em; | |||
| font-style: italic; | |||
| font-weight: 400; | |||
| margin-bottom: -0.2em; | |||
| } | |||
| body.article-detail > header { | |||
| margin-bottom: 3em; | |||
| } | |||
| /* page footer */ | |||
| footer { | |||
| margin: 3em 0; | |||
| } | |||
| footer .nav { | |||
| text-align: center; | |||
| margin-top: 5em; | |||
| margin-bottom: 3.5em; | |||
| } | |||
| footer .nav a { | |||
| padding: 0 0.5em; | |||
| font-size: 1.2em; | |||
| text-decoration: none; | |||
| } | |||
| footer .about { | |||
| border-top: 1px dashed #d2d2d2; | |||
| padding: 2.2em 3em; | |||
| font-size: 0.7em; | |||
| -webkit-column-count: 3; | |||
| -moz-column-count: 3; | |||
| -ms-column-count: 3; | |||
| column-count: 3; | |||
| -webkit-column-gap: 2em; | |||
| -moz-column-gap: 2em; | |||
| -ms-column-gap: 2em; | |||
| column-gap: 2em; | |||
| } | |||
| footer .copy { | |||
| text-align: center; | |||
| font-size: 0.7em; | |||
| font-style: italic; | |||
| margin-top: 1em; | |||
| } | |||
| footer .copy, footer .copy a { | |||
| color: #8e8e8e; | |||
| } | |||
| /* article */ | |||
| .article { | |||
| margin: 3em 0 4em; | |||
| } | |||
| .article header { | |||
| border-top: 1px dashed #d2d2d2; | |||
| } | |||
| .article header h2 { | |||
| font-style: italic; | |||
| text-align: center; | |||
| font-weight: 400; | |||
| margin: 0.8em 0; | |||
| font-size: 1.4em; | |||
| } | |||
| .article header h2 a { | |||
| text-decoration: none; | |||
| } | |||
| .article header .date { | |||
| text-align: center; | |||
| font-size: 0.8em; | |||
| margin-top: -0.7em; | |||
| } | |||
| .article header .date span { | |||
| background-color: #f8f8f8; | |||
| padding: 0 0.7em; | |||
| } | |||
| .article.intro .content p { | |||
| display: inline; | |||
| } | |||
| .article.intro .content .more { | |||
| text-decoration: underline; | |||
| font-weight: 700; | |||
| padding-left: 0.3em; | |||
| } | |||
| .article .content img { | |||
| display: block; | |||
| width: 100%; | |||
| } | |||
| .more, .date { | |||
| font-family: 'Merriweather', serif; | |||
| font-variant: small-caps; | |||
| text-transform: lowercase; | |||
| font-weight: 400; | |||
| text-rendering: auto; | |||
| letter-spacing: 1px; | |||
| } | |||
| /* archive */ | |||
| .archive { | |||
| width: 32em; | |||
| margin: 5em auto 6em; | |||
| padding-left: 2em; | |||
| } | |||
| .archive h2 { | |||
| font-size: 2em; | |||
| margin: 0; | |||
| margin-left: 6.1em; | |||
| margin-bottom: 0.5em; | |||
| font-style: italic; | |||
| } | |||
| .archive a, .archive span{ | |||
| display: block; | |||
| float: left; | |||
| margin-bottom: -1px; | |||
| text-decoration: none; | |||
| } | |||
| .archive li:not(:last-child) { | |||
| border-bottom: 1px solid #d2d2d2; | |||
| margin-bottom: -1px; | |||
| } | |||
| .archive a.last, .archive span.last { | |||
| border: 0; | |||
| margin-bottom: 0; | |||
| } | |||
| .archive a { | |||
| width: 21em; | |||
| text-indent: 1em; | |||
| white-space: nowrap; | |||
| } | |||
| .archive .year-label, | |||
| .archive .month-label{ | |||
| width: 4em; | |||
| font-family: 'Merriweather', serif; | |||
| font-variant: small-caps; | |||
| text-transform: lowercase; | |||
| font-weight: 400; | |||
| text-rendering: auto; | |||
| letter-spacing: 1px; | |||
| text-align: center; | |||
| } | |||
| .archive .month-label { | |||
| width: 7em; | |||
| } | |||
| .archive ul { | |||
| list-style: none; | |||
| margin: 0; | |||
| } | |||
| .archive ul li { | |||
| margin: 0; | |||
| } | |||
| /* code styling */ | |||
| code { | |||
| font-family: 'Anonymous Pro', monospace; | |||
| font-size: 0.85em; | |||
| color: #000; | |||
| } | |||
| pre code { | |||
| display: block; | |||
| line-height: 1.1; | |||
| } | |||
| p code { | |||
| padding: 0.1em 0.3em 0.2em; | |||
| border-radius: 0.3em; | |||
| position: relative; | |||
| top: -0.15em; | |||
| background: #444; | |||
| color: #fff; | |||
| white-space: nowrap; | |||
| } | |||
| /* syntax hl stuff */ | |||
| code.lang-markdown { | |||
| color: #424242; | |||
| } | |||
| code.lang-markdown .header, | |||
| code.lang-markdown .strong { | |||
| font-weight: bold; | |||
| } | |||
| code.lang-markdown .emphasis { | |||
| font-style: italic; | |||
| } | |||
| code.lang-markdown .horizontal_rule, | |||
| code.lang-markdown .link_label, | |||
| code.lang-markdown .code, | |||
| code.lang-markdown .header, | |||
| code.lang-markdown .link_url { | |||
| color: #555; | |||
| } | |||
| code.lang-markdown .blockquote, | |||
| code.lang-markdown .bullet { | |||
| color: #bbb; | |||
| } | |||
| /* Tomorrow Theme */ | |||
| /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ | |||
| /* Original theme - https://github.com/chriskempson/tomorrow-theme */ | |||
| /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ | |||
| .tomorrow-comment, pre .comment, pre .title { | |||
| color: #8e908c; | |||
| } | |||
| .tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo { | |||
| color: #c82829; | |||
| } | |||
| .tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant { | |||
| color: #f5871f; | |||
| } | |||
| .tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute { | |||
| color: #eab700; | |||
| } | |||
| .tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata { | |||
| color: #718c00; | |||
| } | |||
| .tomorrow-aqua, pre .css .hexcolor { | |||
| color: #3e999f; | |||
| } | |||
| .tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title { | |||
| color: #4271ae; | |||
| } | |||
| .tomorrow-purple, pre .keyword, pre .javascript .function { | |||
| color: #8959a8; | |||
| } | |||
| /* media queries */ | |||
| @media (min-width: 1600px) { | |||
| body { font-size: 26px; } | |||
| } | |||
| @media (max-width: 900px) { | |||
| body { font-size: 18px; } | |||
| } | |||
| @media (max-width: 690px) { | |||
| .content-wrap { | |||
| width: auto; | |||
| padding: 0 1em; | |||
| } | |||
| .header { | |||
| margin: 1em 0; | |||
| } | |||
| .header h1 { | |||
| font-size: 1.4em; | |||
| margin-bottom: 0.6em; | |||
| } | |||
| .header .description { | |||
| font-size: 1em; | |||
| } | |||
| .article { | |||
| margin: 1em 0 2.5em; | |||
| } | |||
| .archive { | |||
| width: 80%; | |||
| margin: 0 auto; | |||
| } | |||
| .archive * { | |||
| float: none !important; | |||
| line-height: 1.6 !important; | |||
| width: auto !important; | |||
| height: auto !important; | |||
| text-align: left !important; | |||
| border: 0 !important; | |||
| margin: 0 !important; | |||
| } | |||
| footer .nav { | |||
| margin: 1em 0; | |||
| } | |||
| footer .about { | |||
| padding: 0; | |||
| font-size: 0.9em; | |||
| padding-top: 1.6em; | |||
| -webkit-column-count: 1; | |||
| -moz-column-count: 1; | |||
| -ms-column-count: 1; | |||
| column-count: 1; | |||
| } | |||
| footer .about p { | |||
| margin-bottom: 1em; | |||
| } | |||
| } | |||
| @ -0,0 +1,4 @@ | |||
| { | |||
| "template": "feed.jade", | |||
| "filename": "feed.xml" | |||
| } | |||
| @ -0,0 +1,8 @@ | |||
| { | |||
| "dependencies": { | |||
| "moment": "2.3.x", | |||
| "underscore": "1.4.x", | |||
| "typogr": "0.5.x" | |||
| }, | |||
| "repository": "none" | |||
| } | |||
| @ -0,0 +1,87 @@ | |||
| module.exports = (env, callback) -> | |||
| ### Paginator plugin. Defaults can be overridden in config.json | |||
| e.g. "paginator": {"perPage": 10} ### | |||
| defaults = | |||
| template: 'index.jade' # template that renders pages | |||
| articles: 'articles' # directory containing contents to paginate | |||
| first: 'index.html' # filename/url for first page | |||
| filename: 'page/%d/index.html' # filename for rest of pages | |||
| perPage: 2 # number of articles per page | |||
| # assign defaults any option not set in the config file | |||
| options = env.config.paginator or {} | |||
| for key, value of defaults | |||
| options[key] ?= defaults[key] | |||
| getArticles = (contents) -> | |||
| # helper that returns a list of articles found in *contents* | |||
| # note that each article is assumed to have its own directory in the articles directory | |||
| articles = contents[options.articles]._.directories.map (item) -> item.index | |||
| articles.sort (a, b) -> b.date - a.date | |||
| return articles | |||
| class PaginatorPage extends env.plugins.Page | |||
| ### A page has a number and a list of articles ### | |||
| constructor: (@pageNum, @articles) -> | |||
| getFilename: -> | |||
| if @pageNum is 1 | |||
| options.first | |||
| else | |||
| options.filename.replace '%d', @pageNum | |||
| getView: -> (env, locals, contents, templates, callback) -> | |||
| # simple view to pass articles and pagenum to the paginator template | |||
| # note that this function returns a funciton | |||
| # get the pagination template | |||
| template = templates[options.template] | |||
| if not template? | |||
| return callback new Error "unknown paginator template '#{ options.template }'" | |||
| # setup the template context | |||
| ctx = {@articles, @prevPage, @nextPage} | |||
| # extend the template context with the enviroment locals | |||
| env.utils.extend ctx, locals | |||
| # finally render the template | |||
| template.render ctx, callback | |||
| # register a generator, 'paginator' here is the content group generated content will belong to | |||
| # i.e. contents._.paginator | |||
| env.registerGenerator 'paginator', (contents, callback) -> | |||
| # find all articles | |||
| articles = getArticles contents | |||
| # populate pages | |||
| numPages = Math.ceil articles.length / options.perPage | |||
| pages = [] | |||
| for i in [0...numPages] | |||
| pageArticles = articles.slice i * options.perPage, (i + 1) * options.perPage | |||
| pages.push new PaginatorPage i + 1, pageArticles | |||
| # add references to prev/next to each page | |||
| for page, i in pages | |||
| page.prevPage = pages[i - 1] | |||
| page.nextPage = pages[i + 1] | |||
| # create the object that will be merged with the content tree (contents) | |||
| # do _not_ modify the tree directly inside a generator, consider it read-only | |||
| rv = {pages:{}} | |||
| for page in pages | |||
| rv.pages["#{ page.pageNum }.page"] = page # file extension is arbitrary | |||
| rv['index.page'] = pages[0] # alias for first page | |||
| # callback with the generated contents | |||
| callback null, rv | |||
| # add the article helper to the environment so we can use it later | |||
| env.helpers.getArticles = getArticles | |||
| # tell the plugin manager we are done | |||
| callback() | |||
| @ -0,0 +1,34 @@ | |||
| extends layout | |||
| //- this logic should be moved to a view at some point | |||
| block content | |||
| - var lineHeight = 2.2; | |||
| - var archives = _.chain(env.helpers.getArticles(contents)).groupBy(function(item) { | |||
| - return item.date.getFullYear() | |||
| - }).value() | |||
| - for (var archive in archives) { | |||
| - archives[archive] = _.groupBy(archives[archive], function(item){return item.date.getMonth();}) | |||
| - } | |||
| - var month_names = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] | |||
| section.archive | |||
| h2 Archive | |||
| ul | |||
| - var yearsK = _.chain(archives).keys().reverse().value() | |||
| - for(var year in yearsK) | |||
| - months = archives[yearsK[year]] | |||
| - var yearHeight = lineHeight * _.reduce(months, function(memo,month) { return memo + month.length; }, 0); | |||
| li | |||
| span.year-label(style='line-height:' + yearHeight+'em')=yearsK[year] | |||
| ul(style='margin-left:4em') | |||
| - var monthsK = _.chain(months).keys().reverse().value() | |||
| - for(month in monthsK){ | |||
| - var monthHeight = lineHeight * months[monthsK[month]].length | |||
| li | |||
| span.month-label(style='line-height:'+monthHeight+'em')=month_names[monthsK[month]] | |||
| ul(style='margin-left:7em') | |||
| each item in months[monthsK[month]] | |||
| li(style='height:'+ lineHeight + 'em;line-height:'+ lineHeight + 'em;') | |||
| a(href=item.url)=item.title | |||
| - } | |||
| @ -0,0 +1,25 @@ | |||
| extends layout | |||
| block append vars | |||
| - bodyclass = 'article-detail' | |||
| block prepend title | |||
| | #{ page.title + " - "} | |||
| block header | |||
| include author | |||
| h1= page.title | |||
| p.author | |||
| | #{ "Written by " } | |||
| mixin author(page.metadata.author) | |||
| block content | |||
| article.article | |||
| section.content!= typogr(page.html).typogrify() | |||
| block prepend footer | |||
| div.nav | |||
| a(href=contents.index.url) « Full blog | |||
| @ -0,0 +1,8 @@ | |||
| mixin author(authorName) | |||
| - var author = contents.authors[authorName + '.json'] | |||
| span.author | |||
| if author | |||
| a(href='mailto:'+author.metadata.email)= author.metadata.name | |||
| else | |||
| =authorName | |||
| @ -0,0 +1,25 @@ | |||
| doctype xml | |||
| rss(version='2.0', | |||
| xmlns:content='http://purl.org/rss/1.0/modules/content/', | |||
| xmlns:wfw='http://wellformedweb.org/CommentAPI/', | |||
| xmlns:dc='http://purl.org/dc/elements/1.1/' | |||
| xmlns:atom='http://www.w3.org/2005/Atom') | |||
| channel | |||
| - var articles = env.helpers.getArticles(contents); | |||
| title= locals.name | |||
| atom:link(href=locals.url + '/feed.xml', rel='self', type='application/rss+xml') | |||
| link= locals.url | |||
| description= locals.description | |||
| pubDate= articles[0].rfc822date | |||
| generator Wintersmith - https://github.com/jnordberg/wintersmith | |||
| language en | |||
| for article in articles | |||
| - var permalink = locals.url + article.url | |||
| item | |||
| title= article.title | |||
| link= permalink | |||
| pubDate= article.rfc822date | |||
| guid(isPermaLink='true')= permalink | |||
| author= article.author | |||
| //- passing locals.url resolves all relative urls to absolute | |||
| description= article.getHtml(locals.url) | |||
| @ -0,0 +1,28 @@ | |||
| extends layout | |||
| block content | |||
| include author | |||
| each article in articles | |||
| article.article.intro | |||
| header | |||
| p.date | |||
| span= moment.utc(article.date).format('DD. MMMM YYYY') | |||
| h2 | |||
| a(href=article.url)= article.title | |||
| section.content | |||
| !{ typogr(article.intro).typogrify() } | |||
| if article.hasMore | |||
| p.more | |||
| a(href=article.url) more | |||
| block prepend footer | |||
| div.nav | |||
| if prevPage | |||
| a(href=prevPage.url) « Newer | |||
| else | |||
| a(href='/archive.html') « Archives | |||
| if nextPage | |||
| a(href=nextPage.url) Next page » | |||
| @ -0,0 +1,36 @@ | |||
| !!! 5 | |||
| block vars | |||
| - var bodyclass = null; | |||
| html(lang='en') | |||
| head | |||
| block head | |||
| meta(charset='utf-8') | |||
| meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') | |||
| meta(name='viewport', content='width=device-width') | |||
| title | |||
| block title | |||
| = locals.name | |||
| link(rel='alternate', href=locals.url+'/feed.xml', type='application/rss+xml', title=locals.description) | |||
| link(rel='stylesheet', href='http://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic|Anonymous+Pro:400,700,400italic,700italic|Merriweather:400,700,300') | |||
| link(rel='stylesheet', href='/css/main.css') | |||
| body(class=bodyclass) | |||
| header.header | |||
| div.content-wrap | |||
| block header | |||
| div.logo | |||
| h1 | |||
| a(href=locals.url)= locals.name | |||
| p.description= locals.description | |||
| div#content | |||
| div.content-wrap | |||
| block content | |||
| h2 Welcome to zombocom! | |||
| footer | |||
| div.content-wrap | |||
| block footer | |||
| section.about | |||
| !=contents['about.md'].html | |||
| section.copy | |||
| p © #{ new Date().getFullYear() } #{ locals.owner } — powered by | |||
| a(href='https://github.com/jnordberg/wintersmith') Wintersmith | |||
| //- please leave the "powered by" if you use the design | |||