Full Code of ericzhang-cn/papery for AI

master 830a7efa8cd8 cached
37 files
65.5 KB
23.4k tokens
13 symbols
1 requests
Download .txt
Repository: ericzhang-cn/papery
Branch: master
Commit: 830a7efa8cd8
Files: 37
Total size: 65.5 KB

Directory structure:
gitextract_td6nmx1z/

├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bin/
│   ├── build_runner.js
│   ├── create_runner.js
│   ├── papery.js
│   └── server_runner.js
├── lib/
│   ├── compiler.js
│   └── parser.js
├── package.json
├── startup/
│   ├── articles/
│   │   ├── code-and-math.md
│   │   └── papery-quickstart.md
│   ├── articles.yml
│   ├── assets/
│   │   ├── themes/
│   │   │   └── default/
│   │   │       └── main.css
│   │   └── vendor/
│   │       ├── normalize.css
│   │       ├── prettify-light.css
│   │       ├── prettify-night.css
│   │       └── prettify.js
│   ├── ext.yml
│   ├── navbar.yml
│   ├── pages/
│   │   └── about-me.md
│   ├── pages.yml
│   ├── site.yml
│   └── templates/
│       ├── articles.ejs
│       ├── inc/
│       │   ├── article_footer_plugin.ejs
│       │   ├── footer.ejs
│       │   ├── footer_plugin.ejs
│       │   ├── header.ejs
│       │   ├── header_plugin.ejs
│       │   ├── navbar.ejs
│       │   └── subtitle.ejs
│       ├── index.ejs
│       ├── pages.ejs
│       ├── rss.ejs
│       └── tag.ejs
└── test/
    └── tests.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules
npm-debug.log
.idea


================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - "0.11"
  - "0.10"


================================================
FILE: LICENSE
================================================
Copyright (c) 2013 Eric Zhang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
[![Build Status](https://travis-ci.org/ericzhang-cn/papery.png)](https://travis-ci.org/ericzhang-cn/papery)

Papery - Create your simple, fast & elegant blog with plain text.

# 一分钟生成自己的博客

```bash
npm install -g papery

papery create myblog
papery build myblog
papery server myblog
```

在浏览器中输入 http://localhost:8001/ 即可访问

# papery的特点
+ 纯nodejs编写,跨平台,通过npm直接安装使用
+ 不使用动态脚本,没有数据库
+ 全结构化文本模式(yaml + markdown)发布文章及页面
+ 全静态网站,访问速度飞快,天然防SQL注入等攻击
+ 可定制模板系统,并可方便的扩展
+ 支持自定义皮肤主题
+ 自带代码高亮及LaTeX数学公式支持
+ 可通过插件支持评论、分享、站内推荐等功能

# 使用说明
## 安装及升级
首先要保证机器上安装有[NodeJS](http://nodejs.org/)及[npm](https://npmjs.org/)。

NodeJs版本需要 >= 0.10。

### 安装
```bash
npm install -g papery
```

### 升级
```bash
npm update -g
```

## 命令列表
### create
create命令用于创建一个新的博客,使用方法为:

```bash
papery create <root>
```

执行后则会在root目录创建一个全新的博客,里面包含papery博客的基本目录结构及配置文件等内容。详细信息会在下文详述。

### build
通过create创建的博客还不能成为一个真正可以访问的网站,因为里面只包含配置信息和元文本,还没有web页面。build用于根据配置和元文本生成web内容。使用方法为:

```bash
papery build <root>
```

### server
server可以在本地启动一个调试服务器用于快速预览和调试内容,命令为:

```bash
papery server <root> [<port>]
```

执行上述命令将在本地port指定的端口启动一个webserver,其中port为可选项,默认值为8001。在浏览器中输入 http://localhost:<port>/ 即可访问。

## 目录结构
一个papery博客的目录结构如下

```bash
root
 | - articles.yml #文章配置
 | - ext.yml      #用户自定义扩展配置
 | - navbar.yml   #导航菜单配置
 | - pages.yml    #独立页面配置
 | - site.yml     #站点主配置
 | - index.html   #首页(自动生成)
 | - rss.xml      #RSS订阅源(自动生成)
 | - tag.html     #标签索引页(自动生成)
 | - articles #放置文章的目录
      |- post1.md    #post1元文本
      |- post1.html  #post1文章页面(自动生成)
      |- post2.md    #post2元文本
      |- post2.html  #post2文章页面(自动生成)
 | - pages #放置独立页面的目录
      |- page1.md    #page1元文本
      |- page1.html  #page1独立页面(自动生成)
 | - assets #资源目录
      |- vendor  #第三方资源
      |- themes  #主题
          |- default #默认主题
 | - templates #模板目录
```

## 配置站点
站点的总配置文件是site.yml。papery使用[yaml](http://www.yaml.org/)格式作为配置文件格式。
由于yaml的配置格式非常简洁且具有较高的自解释能力,因此即使你没接触过yaml也可以很快理解配置的意义。

通过create创建的默认site.yml内容如下:

```yaml
title: Title of blog
subtitle: Subtitle of blog
link: Homepage link
meta:
  description: Content of description meta tag
  keywords: !!seq
    - keyword1
    - keyword2
    - keyword3
  author: Content of author meta tag
master:
  name: Your name
  about: Introduce yourself here
  email: Your E-mail
rss:
  title: RSS feed title
  desc: RSS feed description
  lang: RSS feed language (ex: zh-cn)
  max: 10
copyright:
  owner: Copyrighter
  beginYear: 2011
  endYear: 2013
theme: default
codetheme: night
```

其中每个字段的意义已经标示清楚,按照自己的需求修改即可。

## 写文章
papery中的文章有两部分组成:文章配置及元文本。文章配置用于告诉papery的构建系统文章的一些信息,而元文本则是文章的内容。

### 文章配置
文章配置文件为articles.yml。其中一篇文章的配置格式如下:

```yaml
- id: post-id
  title: 文章标题
  postedOn: !!str 2013-01-01
  author: 作者
  tags:
    - 标签1
    - 标签2
  abstract: 摘要内容
```

注意其中最重要的配置项是id。id作为文章的唯一标识,要求在整个articles.yml配置的所有文章中唯一,并且只能包含小写英文字母、数字和中横“-”。

id不但指定了元文本的名称,而且会成为文章permalink的。建议的id写法是文章的英文标题按单词用“-”连接。例如“papery-quickstart”。

### 元文本
元文本是文章的内容,papery根据元文本和文章配置最终生成文章页面。papery使用[GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown)(简称gfm)作为元文本书写格式。gfm基本保持了标准markdown的功能,同时增加了一些新的特性,文档见[这里](https://help.github.com/articles/github-flavored-markdown)。

一篇元文本是放在articles目录下以“md”为后缀名的文件,注意元文本的名字对应配置中id字段的名字。例如“id: papery-quickstart”的文章对应的元文本为articles/papery-quickstart.md。

你可以用任何文本编辑器书写元文本。如果某些地方markdown的表达能力不够,你可以直接插入html代码。papery元文本支持markdown与html混编。

## 写独立页面
博客中有时需要一些如“关于”等独立页面。独立页面的编写与文章非常类似,也是用yaml编写配置文件,用gfm编写元文本。

独立页面的配置文件为pages.yml,元文本放在pages/目录下。

独立页面的配置项只有id和title,同样通过id关联配置项和元文本。

## 配置导航菜单
papery默认的导航菜单项只有“首页”和“标签”。如果要增加新的导航菜单项,则需要在navbar.yml中配置。例如我们配置两个导航菜单,一个到博客的RSS订阅源,一个到github首页,则可以如下配置navbar.yml

```yaml
- label: 订阅
  href: /rss.xml
  target: _self

- label: github
  href: https://github.com
  target: _blank
```

注意target配置项可以配置此导航链接是在本窗口打开还是在新窗口打开。

## 代码高亮
papery内置代码高亮支持,高亮通过[Google Code Prettify](https://code.google.com/p/google-code-prettify/)实现。

插入代码时使用gfm格式,用“\`\`\`[lang]”开头,并用“\`\`\`”结尾,如:

<pre>
```c
#include <stdio.h>

int main(int argc, char** argv) {
    printf("%s\n", "Hello, World!");
}
```
</pre>

即可实现代码高亮。

papery自带两种代码高亮风格,分别是“night”和“light”。默认为“night”,可以在site.yml的“codetheme”中配置。

## 数学公式
papery默认启用[MathJax](http://www.mathjax.org/)插件,因此直接支持LaTeX格式的数学公式渲染。不过由于反斜杠“\”和下划线“_”在markdown中有特殊意义,因此需要转义。

### 内联数学公式
内联数学公式使用“$...$”或“\\(...\\)”包裹,渲染后内联于行内。例如:

```
\\(e^{i\\pi}+1=0\\)
$e^{i\\pi}+1=0$
```

注意反斜杠需要转义。另外,如果需要将“$”解释为字符本身而非Tex数学公式,可以使用转义字符,如:

```
\\$2.50
```

### 单行数学公式
单行数学公式使用“$$...$$”或“\\[...\\]”包裹,渲染后单独占一行,例如:

```
\\[e^{i\\pi}+1=0\\]
$$e^{i\\pi}+1=0$$
```

## TOC
在文中任何位置插入

```html
<!-- toc -->
```

会在当前位置根据文章outline自动生成TOC。

# 高级使用
## 扩展配置及自定义模板
### 自定义模板
papery使用[ejs](https://github.com/visionmedia/ejs)作为模板引擎。模板文件全部放在templates目录下,后缀名为ejs。对于有html基础的用户可以自己对模板进行定制。

## 扩展配置项绑定
除了固定配置项外,papery还提供了一个ext.yml用于用户自定义扩展配置。用户在这个yaml中可以输入自己的配置,然后在模板中通过ext命名空间绑定内容。

例如,在ext.yml中输入:

```yaml
foo: bar
```

则在模板文件中用

```html
<%= ext.foo %>
```

则此处内容会被替换为“bar”。结合yaml的数据结构及ejs模板引擎,有编程基础的用户可以灵活的按需定制。

## 自定义主题
papery的模板中没有表现相关的东西,最终的外观表现依赖于皮肤主题。皮肤主题存放在assets/themes/目录下,子目录名称就是主题名称。当前启用的主题在site.yml的theme配置项中配置。

papery默认带一个名叫“default”的主题。

自定义主题的主文件是assets/themes/[theme_name]/main.css文件。用户可以通过写不同的main.css文件放在相应目录下,然后修改site.yml来启用不同主题。

如需引入额外css、js或图片文件,请使用下文提到的插件模式。

## 插件
papery通过在模板文件中引入不同的模板片段启用不同插件。启用插件的方式是将相关代码片段放到相关的注入点模板文件即可。默认有三个注入点:

### header\_plugin
header\_plugin的模板文件为templates/inc/header\_plugin.ejs。这个文件的内容会被包含到网站所有页面的head部分内。可以用于引入一些在页面主内容加载前需要引入的css、js等。如皮肤主题需要的额外css。

### footer\_plugin
footer\_plugin的模板文件为templates/inc/footer\_plugin.ejs。这个文件的内容会被包含到网站所有页面的body部分结束前。可以用于引入一些在页面主内容加载后需要引入的css、js等。如网站统计代码。

papery默认启用的mathjax插件在这里引入。

### article\_footer\_plugin
article\_footer\_plugin的模板文件为templates/inc/article\_footer\_plugin.ejs。这个文件的内容被包含到所有文章页面的底部。可以用于引入评论、分享等于文章相关的插件。

如上述位置不满足需求,用户也可以通过自定义模板方式自己定制页面。

### 常用插件推荐
#### 评论
+ 多说 - http://duoshuo.com
+ 友言 - http://www.uyan.cc
+ 畅言 - http://changyan.sohu.com

#### 社会化分享
+ JiaThis - http://www.jiathis.com
+ bShare - http://www.bshare.cn
+ 百度分享 - http://share.baidu.com

#### 推荐系统
+ 友荐 - http://www.ujian.cc

#### 统计
+ Google Analytics - http://www.google.com/analytics
+ 百度统计 - http://tongji.baidu.com
+ 量子恒道 - http://www.linezing.com
+ 腾讯分析 - http://ta.qq.com

# 开发
## 代码库
```bash
git clone https://github.com/ericzhang-cn/papery.git
```

## 运行单元测试
```bash
cd papery
npm test
```

# License
[The MIT License (MIT)](http://opensource.org/licenses/MIT)

Copyright (c) 2013 Eric Zhang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: bin/build_runner.js
================================================
/**
 * 编译整个站点
 */

var _ = require('underscore');
var compiler = require('../lib/compiler');

function _help() {
    console.log('Usage: papery build <root>');
    console.log('    root - Root directory of blog');
}

exports.help = function () {
    _help();
};

exports.run = function (options) {
    var root;
    if (_.size(options) >= 1) {
        root = options[0];
    } else {
        _help();
        return;
    }

    compiler.compile(root);
};



================================================
FILE: bin/create_runner.js
================================================
/**
 * 生成一个新的papery站点
 */

var _ = require('underscore');
var fs = require('fs');
var fse = require('fs-extra');
var log4js = require('log4js');
var logger = log4js.getLogger();

function _help() {
    console.log('Usage: papery create <root>');
    console.log('    root - Root directory of blog');
}

exports.help = function () {
    _help();
};

exports.run = function (options) {
    var root;
    if (_.size(options) >= 1) {
        root = options[0];
    } else {
        _help();
        return;
    }

    var startup = __dirname + '/../startup';
    if (!fs.existsSync(startup)) {
        logger.error('Startup templates can not be found! Please reinstall your papery');
        process.exit(1);
    }

    var src = startup;
    var dest = root;
    fse.copySync(src, dest);

    logger.info('Woo! A new blog was born in ' + dest);
};



================================================
FILE: bin/papery.js
================================================
#!/usr/bin/env node

/**
 * 程序入口
 */

var _ = require('underscore');

var cmds = [ 'create', 'build', 'server' ];
var runners = [];
_.each(cmds, function (cmd) {
    runners[cmd] = require('./' + cmd + '_runner');
});

var help = function () {
    console.log('Usage:');
    console.log('    papery -v or --version');
    console.log('    papery -h or --help');
    console.log('    papery command [options]');
    console.log('    papery help command');
    console.log('');
    console.log('Commands:');
    console.log('    create - Create a new blog');
    console.log('    build  - Build all site from config and meta-text');
    console.log('    server - Start a new local web server for test and debug');
};

var version = function () {
    var pack = require('../package.json');
    console.log('Version: ' + pack.version);
}

var showHelp = function (cmd) {
    runners[cmd].help();
};

var checkArgs = function (args) {
    if (args.length >= 2) {
        if (args[1] === '-v' || args[1] === '--version') {
            version();
            process.exit(1);
        }
        if (args[1] === '-h' || args[1] === '--help') {
            help();
            process.exit(1);
        }
        if (args.length >= 3 && args[1] === 'help' && _.contains(cmds, args[2])) {
            showHelp(args[2]);
            process.exit(1);
        }
        if (!_.contains(cmds, args[1])) {
            help();
            process.exit(1);
        }
        return 0;
    } else {
        help();
        process.exit(1);
    }
};

var args = !(process.argv[0] === 'node' ||
    process.argv[0] === 'nodejs' ||
    process.argv[0].match(/node.exe$/) ||
    process.argv[0].match(/node$/)) ? process.argv : _.last(process.argv, process.argv.length - 1);

checkArgs(args);
var cmd = args[1];
var options = _.last(args, args.length - 2);

runners[cmd].run(options);



================================================
FILE: bin/server_runner.js
================================================
/**
 * 在本地启动一个调试服务器
 */

var _ = require('underscore');
var connect = require('connect');
var log4js = require('log4js');
var logger = log4js.getLogger();

function _help () {
    console.log('Usage: papery server <root> <[port]>');
    console.log('    root - Root directory of blog');
    console.log('    port - Port to start web server, must greater than 3000, default is 8001');
}

exports.help = function () {
    _help();
};

exports.run = function (options) {
    var port = 8001;
    var root;
    if (_.size(options) >= 2) {
        root = options[0];
        port = parseInt(options[1], 10);
    } else if (_.size(options) === 1) {
        root = options[0];
    } else {
        _help();
        return;
    }

    if (port < 3000) {
        log.error('Port number must greater than 3000');
        return;
    }

    connect.createServer(
        connect.static(root)
    ).listen(port);

    logger.info('Server started at http://localhost:' + port + '/');
}


================================================
FILE: lib/compiler.js
================================================
/**
 * 生成编译后的静态页面文件
 * @module lib/compiler
 */

var fs = require('fs');
var ejs = require('ejs');
var marked = require('marked');
var parser = require('./parser');
var log4js = require('log4js');
var toc = require('marked-toc');
var logger = log4js.getLogger();

// 自定义Render
var renderer = new marked.Renderer();
// 原始marked生成anchor时不支持中文,这里加入中文支持,以便与TOC相匹配
renderer.heading = function (text, level) {
    return '<h'
        + level
        + ' id="'
        + this.options.headerPrefix
        + text.toLowerCase().replace(/\./g, '').replace(/[^[\w\u0100-\uffff\]]+/g, '-')
        + '">'
        + text
        + '</h'
        + level
        + '>\n';
}

// 开启GFM并支持代码高亮
marked.setOptions({
    gfm: true,
    langPrefix: 'prettyprint linenums lang-',
    renderer: renderer
});

/**
 * 读取ejs模板文件
 * @param  {String} root papery根目录
 * @param  {String} tpl  ejs模板文件名称
 * @return {String}      模板文件内容
 */
var loadTemplate = function (root, tpl) {
    var path = root + 'templates/' + tpl + '.ejs';
    if (!fs.existsSync(path)) {
        logger.error(path + ' can not be found!');
        process.exit(1);
    }

    var tplContent = fs.readFileSync(path, 'utf8');
    logger.info('Load ' + path + ' completed!');

    return tplContent;
};

/**
 * 生成首页
 * @param  {String} root papery根目录
 * @param  {Object} opts 配置对象
 */
exports.compileIndex = function (root, opts) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var tpl = loadTemplate(root, 'index');

    if (typeof opts !== 'object') {
        opts = parser.parseYaml(root);
    }
    opts.filename = root + 'templates/index.ejs';

    var html = ejs.render(tpl, opts);

    fs.writeFileSync(root + '/index.html', html);

    logger.info('Build index.html completed!');
};

/**
 * 生成RSS
 * @param  {String} root papery根目录
 * @param  {Object} opts 配置对象
 */
exports.compileRss = function (root, opts) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var tpl = loadTemplate(root, 'rss');

    if (typeof opts !== 'object') {
        opts = parser.parseYaml(root);
    }
    opts.filename = root + 'templates/rss.ejs';

    opts.site.rss.items.forEach(function (item) {
        var pathMd = root + 'articles/' + item.id + '.md',
            pathHt = root + 'articles/' + item.id + '.ht';
        if (!fs.existsSync(pathMd) && !fs.existsSync(pathHt)) {
            logger.warn(pathMd + ' can not be found!');
            return;
        }
        var path = fs.existsSync(pathMd) ? pathMd : pathHt;

        var content = fs.readFileSync(path, 'utf8');
        if (path.slice(-2) === 'md') {
            content = marked(content);
            content = content.replace(/<pre><code/g, '<pre');
            content = content.replace(/<\/code><\/pre>/g, '<\/pre>');
        }
        item.description = content;
    });

    var html = ejs.render(tpl, opts);

    fs.writeFileSync(root + '/rss.xml', html);

    logger.info('Build rss.xml completed!');
};

/**
 * 生成标签页
 * @param  {String} root papery根目录
 * @param  {Object} opts 配置对象
 */
exports.compileTag = function (root, opts) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var tpl = loadTemplate(root, 'tag');

    if (typeof opts !== 'object') {
        opts = parser.parseYaml(root);
    }
    opts.filename = root + 'templates/tag.ejs';

    var html = ejs.render(tpl, opts);

    fs.writeFileSync(root + '/tag.html', html);

    logger.info('Build tag.html completed!');
};

/**
 * 生成独立页面
 * @param  {String} root papery根目录
 * @param  {Object} opts 配置对象
 */
exports.compilePages = function (root, opts) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var tpl = loadTemplate(root, 'pages');

    if (typeof opts !== 'object') {
        opts = parser.parseYaml(root);
    }
    opts.filename = root + 'templates/pages.ejs';

    opts.pages.forEach(function (page) {
        var path = root + 'pages/' + page.id + '.md';
        if (!fs.existsSync(path)) {
            logger.warn(path + ' can not be found!');
            return;
        }

        var content = marked(fs.readFileSync(path, 'utf8'));
        content = content.replace(/<pre><code/g, '<pre');
        content = content.replace(/<\/code><\/pre>/g, '<\/pre>');
        opts.page = {
            title: page.title,
            content: content
        };

        var html = ejs.render(tpl, opts);
        var output = root + 'pages/' + page.id + '.html';
        fs.writeFileSync(output, html);
        logger.info('Build ' + output + ' completed!');
    });
};

/**
 * 生成文章页面
 * @param  {String} root papery根目录
 * @param  {Object} opts 配置对象
 */
exports.compileArticles = function (root, opts) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var tpl = loadTemplate(root, 'articles');

    if (typeof opts !== 'object') {
        opts = parser.parseYaml(root);
    }
    opts.filename = root + 'templates/articles.ejs';

    opts.articles.forEach(function (ar) {
        var pathMd = root + 'articles/' + ar.id + '.md',
            pathHt = root + 'articles/' + ar.id + '.ht';
        if (!fs.existsSync(pathMd) && !fs.existsSync(pathHt)) {
            logger.warn(pathMd + ' can not be found!');
            return;
        }
        var path = fs.existsSync(pathMd) ? pathMd : pathHt;

        var content = fs.readFileSync(path, 'utf8');
        if (path.slice(-2) === 'md') {
            content = toc.insert(content);
            content = marked(content);
            content = content.replace(/<pre><code/g, '<pre');
            content = content.replace(/<\/code><\/pre>/g, '<\/pre>');
        }
        ar.content = content;
        ar.link = '/articles/' + ar.id + '.html';
        opts.article = ar;

        var html = ejs.render(tpl, opts);
        var output = root + 'articles/' + ar.id + '.html';
        fs.writeFileSync(output, html);
        logger.info('Build ' + output + ' completed!');
    });
};

/**
 * 生成整个网站
 * @param  {String} root papery根目录
 */
exports.compile = function (root) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var opts = parser.parseYaml(root);

    this.compileIndex(root, opts);
    this.compileRss(root, opts);
    this.compileTag(root, opts);
    this.compilePages(root, opts);
    this.compileArticles(root, opts);
};


================================================
FILE: lib/parser.js
================================================
/**
 * 解析YAML配置文件信息
 * @module lib/parser
 */

var _ = require('underscore');
var fs = require('fs');
var moment = require('moment');
var yaml = require('js-yaml');
var log4js = require('log4js');
var logger = log4js.getLogger();

/**
 * 所有YAML配置文件名称
 * @type {Array}
 */
var yamls = ['site', 'articles', 'pages', 'navbar', 'ext'];

/**
 * 根据articles配置生成标签
 * @param  {Object} obj 配置对象
 * @return {Object}     生成标签后的对象
 */
var generateTags = function (obj) {
    var m = {};
    obj.articles.forEach(function (article) {
        article.tags.forEach(function (tag) {
            if (! _.has(m, tag)) {
                m[tag] = [];
            }
            m[tag].push(article);
        });
    });
    obj.tags = [];
    obj.coreTags = [];
    _.keys(m).forEach(function (tag) {
        obj.tags.push({
            label: tag,
            count: m[tag].length,
            articles: m[tag]
        });
        if (m[tag].length > 1) {
            obj.coreTags.push({
                label: tag,
                count: m[tag].length
            });
        }
    });

    obj.tags = _.sortBy(obj.tags, function (tag) {
        return -tag.count;
    });
    obj.coreTags = _.sortBy(obj.coreTags, function (tag) {
        return -tag.count;
    });

    return obj;
};

/**
 * 生成RSS内容
 * @param  {Object} obj 配置对象
 * @return {Object}     生成RSS后的配置对象
 */
var generateRss = function (obj) {
    obj.site.rss.lastBuildDate = moment().format('ddd, DD MMM YYYY HH:mm:ss') + ' +0800';
    obj.site.rss.items = [];
    obj.articles.slice(0, obj.site.rss.max).forEach(function (article) {
        obj.site.rss.items.push({
            id: article.id,
            title: article.title,
            link: obj.site.link + '/articles/' + article.id + '.html?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss',
            guid: obj.site.link + '/articles/' + article.id + '.html',
            author: obj.site.master.email + ' ' + obj.site.master.name,
            pubDate: moment(article.postedOn).format('ddd, DD MMM YYYY HH:mm:ss') + ' +0800',
            description: ''
        });
    });

    return obj;
}

/**
 * 读取YAML配置并生成配置对象
 * @param  {String} root papery根目录
 * @return {Object}      配置对象
 */
exports.parseYaml = function (root) {
    if (root.slice(-1) !== '/') {
        root += '/';
    }

    var obj = {};
    yamls.forEach(function (y) {
        var path = root + y + '.yml';

        if (!fs.existsSync(path)) {
            logger.error(path + ' can not be found!');
            process.exit(1);
        }

        obj[y] = yaml.safeLoad(fs.readFileSync(path, 'utf8'));
        logger.info('Load ' + path + ' completed!');
    });

    obj = generateTags(obj);
    obj = generateRss(obj);

    return obj;
};


================================================
FILE: package.json
================================================
{
  "name": "papery",
  "version": "0.2.5-2",
  "description": "create your simple, fast & elegant blog with plain text",
  "bin": {
      "papery": "./bin/papery.js"
  },
  "dependencies": {
    "marked":     "^0.3.2",
    "moment":     "^2.1.0",
    "underscore": "^1.5.1",
    "js-yaml":    "^2.1.0",
    "ejs":        "^0.8.4",
    "log4js":     "^0.6.7",
    "connect":    "^2.8.5",
    "fs-extra":   "^0.8.1",
    "marked-toc": "^0.2.6"
  },
  "devDependencies": {
    "mocha": "^1.12.0",
    "chai":  "^1.7.2"
  },
  "scripts": {
    "test": "mocha ./test/tests.js -R spec"
  },
  "repository": {
    "type": "git",
    "url": "git://github.com/ericzhang-cn/papery.git"
  },
  "author": "Eric Zhang",
  "license": "MIT",
  "readmeFilename": "README.md"
}


================================================
FILE: startup/articles/code-and-math.md
================================================
这是一段代码
===========

```c
#include <stdio.h>

int main(int argc, char** argv) {
    printf("%s\n", "Hello, World!");
}
```

这是一些数学公式
===============

\\[e^{i\\pi}+1=0\\]

\\[\\begin{pmatrix}
  1/\\sqrt{2}  & 1/\\sqrt{2} \\\\
  -1/\\sqrt{2} & 1/\\sqrt{2}
\\end{pmatrix}
\\begin{pmatrix}
  3 \\\\
  2
\\end{pmatrix}
=
\\begin{pmatrix}
  5/\\sqrt{2} \\\\
  -1/\\sqrt{2}
\\end{pmatrix}\\]

\\[Var(a)=\\frac{1}{m}\\sum\_{i=1}^m{(a\_i-\\mu)^2}\\]


================================================
FILE: startup/articles/papery-quickstart.md
================================================
Papery - create your simple, fast & elegant blog with plain text.

# 使用说明
## 安装及升级
首先要保证机器上安装有[NodeJS](http://nodejs.org/)及[npm](https://npmjs.org/)。

NodeJs版本需要>=0.10。

### 安装
```bash
npm install -g papery
```

### 升级
```bash
npm update -g
```

## 命令列表
### create
create命令用于创建一个新的博客,使用方法为:

```bash
papery create <root>
```

执行后则会在root目录创建一个全新的博客,里面包含papery博客的基本目录结构及配置文件等内容。详细信息会在下文详述。

### build
通过create创建的博客还不能成为一个真正可以访问的网站,因为里面只包含配置信息和元文本,还没有web页面。build用于根据配置和元文本生成web内容。使用方法为:

```bash
papery build <root>
```

### server
server可以在本地启动一个调试服务器用于快速预览和调试内容,命令为:

```bash
papery server <root> [<port>]
```

执行上述命令将在本地port指定的端口启动一个webserver,其中port为可选项,默认值为8001。在浏览器中输入 http://localhost:<port>/ 即可访问。

## 目录结构
一个papery博客的目录结构如下

```bash
root
 | - articles.yml #文章配置
 | - ext.yml      #用户自定义扩展配置
 | - navbar.yml   #导航菜单配置
 | - pages.yml    #独立页面配置
 | - site.yml     #站点主配置
 | - index.html   #首页(自动生成)
 | - rss.xml      #RSS订阅源(自动生成)
 | - tag.html     #标签索引页(自动生成)
 | - articles #放置文章的目录
      |- post1.md    #post1元文本
      |- post1.html  #post1文章页面(自动生成)
      |- post2.md    #post2元文本
      |- post2.html  #post2文章页面(自动生成)
 | - pages #放置独立页面的目录
      |- page1.md    #page1元文本
      |- page1.html  #page1独立页面(自动生成)
 | - assets #资源目录
      |- vendor  #第三方资源
      |- themes  #主题
          |- default #默认主题
 | - templates #模板目录
```

## 配置站点
站点的总配置文件是site.yml。papery使用[yaml](http://www.yaml.org/)格式作为配置文件格式。
由于yaml的配置格式非常简洁且具有较高的自解释能力,因此即使你没接触过yaml也可以很快理解配置的意义。

通过create创建的默认site.yml内容如下:

```yaml
title: Title of blog
subtitle: Subtitle of blog
link: Homepage link
meta:
  description: Content of description meta tag
  keywords: !!seq
    - keyword1
    - keyword2
    - keyword3
  author: Content of author meta tag
master:
  name: Your name
  about: Introduce yourself here
  email: Your E-mail
rss:
  title: RSS feed title
  desc: RSS feed description
  lang: RSS feed language (ex: zh-cn)
  max: 10
copyright:
  owner: Copyrighter
  beginYear: 2011
  endYear: 2013
theme: default
codetheme: night
```

其中每个字段的意义已经标示清楚,按照自己的需求修改即可。

## 写文章
papery中的文章有两部分组成:文章配置及元文本。文章配置用于告诉papery的构建系统文章的一些信息,而元文本则是文章的内容。

### 文章配置
文章配置文件为articles.yml。其中一篇文章的配置格式如下:

```yaml
- id: post-id
  title: 文章标题 
  postedOn: !!str 2013-01-01
  author: 作者
  tags: 
    - 标签1
    - 标签2
  abstract: 摘要内容 
```

注意其中最重要的配置项是id。id作为文章的唯一标识,要求在整个articles.yml配置的所有文章中唯一,并且只能包含小写英文字母、数字和中横“-”。

id不但指定了元文本的名称,而且会成为文章permalink的。建议的id写法是文章的英文标题按单词用“-”连接。例如“papery-quickstart”。

### 元文本
元文本是文章的内容,papery根据元文本和文章配置最终生成文章页面。
papery使用[GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown)(简称gfm)作为元文本书写格式。
gfm基本保持了标准markdown的功能,同时增加了一些新的特性,文档见[这里](https://help.github.com/articles/github-flavored-markdown)。

一篇元文本是放在articles目录下以“md”为后缀名的文件,注意元文本的名字对应配置中id字段的名字。例如“id: papery-quickstart”的文章对应的元文本为articles/papery-quickstart.md。

你可以用任何文本编辑器书写元文本。如果某些地方markdown的表达能力不够,你可以直接插入html代码。papery元文本支持markdown与html混编。

## 写独立页面
博客中有时需要一些如“关于”等独立页面。独立页面的编写与文章非常类似,也是用yaml编写配置文件,用gfm编写元文本。

独立页面的配置文件为pages.yml,元文本放在pages/目录下。

独立页面的配置项只有id和title,同样通过id关联配置项和元文本。

## 配置导航菜单
papery默认的导航菜单项只有“首页”和“标签”。如果要增加新的导航菜单项,则需要在navbar.yml中配置。例如我们配置两个导航菜单,一个到博客的RSS订阅源,一个到github首页,则可以如下配置navbar.yml

```yaml
- label: 订阅
  href: /rss.xml
  target: _self

- label: github
  href: https://github.com
  target: _blank
```

注意target配置项可以配置此导航链接是在本窗口打开还是在新窗口打开。

## 代码高亮
papery内置代码高亮支持,高亮通过[Google Code Prettify](https://code.google.com/p/google-code-prettify/)实现。

插入代码时使用gfm格式,用“\`\`\`[lang]”开头,并用“\`\`\`”结尾,如:

<pre>
```c
#include <stdio.h>

int main(int argc, char** argv) {
    printf("%s\n", "Hello, World!");
}
```
</pre>

即可实现代码高亮。效果:

```c
#include <stdio.h>

int main(int argc, char** argv) {
    printf("%s\n", "Hello, World!");
}
```

papery自带两种代码高亮风格,分别是“night”和“light”。默认为“night”,可以在site.yml的“codetheme”中配置。

## 数学公式
papery默认启用[MathJax](http://www.mathjax.org/)插件,因此直接支持LaTeX格式的数学公式渲染。不过由于反斜杠“\”和下划线“_”在markdown中有特殊意义,因此需要转义。

### 内联数学公式
内联数学公式使用“\$...\$”或“\\\\(...\\\\)”包裹,渲染后内联于行内。例如:

<pre>
\\(e^{i\\pi}+1=0\\)
$e^{i\\pi}+1=0$
</pre>

效果:$e^{i\\pi}+1=0$

注意反斜杠需要转义。另外,如果需要将“$”解释为字符本身而非Tex数学公式,可以使用转义字符,如:

<pre>
\$2.50
</pre>

### 单行数学公式
单行数学公式使用“\$\$...\$\$”或“\\\\[...\\\\]”包裹,渲染后单独占一行,例如:

<pre>
\\[e^{i\\pi}+1=0\\]
$$e^{i\\pi}+1=0$$
</pre>

效果:
$$e^{i\\pi}+1=0$$

## TOC
效果:
<!-- toc -->

在文中任何位置插入

```html
<!-- toc -->
```

会在当前位置根据文章outline自动生成TOC。

# 高级使用
## 扩展配置及自定义模板
### 自定义模板
papery使用[ejs](https://github.com/visionmedia/ejs)作为模板引擎。模板文件全部放在templates目录下,后缀名为ejs。对于有html基础的用户可以自己对模板进行定制。

## 扩展配置项绑定
除了固定配置项外,papery还提供了一个ext.yml用于用户自定义扩展配置。用户在这个yaml中可以输入自己的配置,然后在模板中通过ext命名空间绑定内容。

例如,在ext.yml中输入:

```yaml
foo: bar
```

则在模板文件中用

```html
<%= ext.foo %>
```

则此处内容会被替换为“bar”。结合yaml的数据结构及ejs模板引擎,有编程基础的用户可以灵活的按需定制。

## 自定义主题
papery的模板中没有表现相关的东西,最终的外观表现依赖于皮肤主题。皮肤主题存放在assets/themes/目录下,子目录名称就是主题名称。当前启用的主题在site.yml的theme配置项中配置。

papery默认带一个名叫“default”的主题。

自定义主题的主文件是assets/themes/[theme_name]/main.css文件。用户可以通过写不同的main.css文件放在相应目录下,然后修改site.yml来启用不同主题。

如需引入额外css、js或图片文件,请使用下文提到的插件模式。

## 插件
papery通过在模板文件中引入不同的模板片段启用不同插件。启用插件的方式是将相关代码片段放到相关的注入点模板文件即可。默认有三个注入点:

### header\_plugin
header\_plugin的模板文件为templates/inc/header\_plugin.ejs。这个文件的内容会被包含到网站所有页面的head部分内。可以用于引入一些在页面主内容加载前需要引入的css、js等。如皮肤主题需要的额外css。

### footer\_plugin
footer\_plugin的模板文件为templates/inc/footer\_plugin.ejs。这个文件的内容会被包含到网站所有页面的body部分结束前。可以用于引入一些在页面主内容加载后需要引入的css、js等。如网站统计代码。

papery默认启用的mathjax插件在这里引入。

### article\_footer\_plugin
article\_footer\_plugin的模板文件为templates/inc/article\_footer\_plugin.ejs。这个文件的内容被包含到所有文章页面的底部。可以用于引入评论、分享等于文章相关的插件。

如上述位置不满足需求,用户也可以通过自定义模板方式自己定制页面。

### 常用插件推荐
#### 评论
+ 多说 - http://duoshuo.com
+ 友言 - http://www.uyan.cc
+ 畅言 - http://changyan.sohu.com

#### 社会化分享
+ JiaThis - http://www.jiathis.com
+ bShare - http://www.bshare.cn
+ 百度分享 - http://share.baidu.com

#### 推荐系统
+ 友荐 - http://www.ujian.cc

#### 统计
+ Google Analytics - http://www.google.com/analytics
+ 百度统计 - http://tongji.baidu.com
+ 量子恒道 - http://www.linezing.com
+ 腾讯分析 - http://ta.qq.com

# 开发
## 代码库
```bash
git clone https://github.com/ericzhang-cn/papery.git
```

## 运行单元测试
```bash
cd papery
npm test
```

# License
[The MIT License (MIT)](http://opensource.org/licenses/MIT)

Copyright (c) 2013 Eric Zhang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: startup/articles.yml
================================================
- id: papery-quickstart
  title: Papery快速指南
  postedOn: !!str 2013-01-02
  author: Papery
  tags: 
    - Papery
    - 指南
  abstract: 快速了解如何使用Papery。

- id: code-and-math
  title: 代码与数学公式示例
  postedOn: !!str 2013-01-01
  author: Papery
  tags:
    - Papery
    - 代码
    - 数学公式
  abstract: 展示了一些代码和数学公式显示效果。


================================================
FILE: startup/assets/themes/default/main.css
================================================
/* 基础而通用的样式 */
body {
    font-family: "Helvetica Neue", Helvetica, Arial, "Hiragino Sans GB", "Hiragino Sans GB W3", "WenQuanYi Micro Hei", "Microsoft YaHei UI", "Microsoft YaHei", sans-serif;
}
a {
    text-decoration: none;
    color: #08c;
}
a:hover {
    text-decoration: underline;
}
p {
    font-size: 16px;
    line-height: 1.8em;
    color: #5a5a5a;
}
li {
    line-height: 1.8em;
}
h1, h2, h3, h4 {
    font-weight: normal;
}

/* 为代码块定义的样式 */
pre.prettyprint {
    font-family: Monaco,Consolas,Monospace,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New','宋体';
    font-size: 14px;
    border: 1px solid #e1e1e8;
    border-radius: 4px;
}
pre.prettyprint ol {
    margin: 0;
}

/* 页面头部 */
div#header {
    background-color: #2a3440;
    height: 50px;
}
div#header-inner {
    width: 90%;
    max-width: 1024px;
    margin: 0 auto;
    padding-top: 2px;
}
div#main-inner {
    width: 90%;
    max-width: 1024px;
    margin: 0 auto;
}
div#title a {
    font-weight: bold;
    font-size: 24px;
    color: #fff;
}
div#subtitle {
    color: rgba(255,255,255,.5);
    font-weight: bold;
    font-size: 11px;
}

/* 页面底部 */
div#footer-inner {
    border-top: 1px solid #5a5a5a;
    width: 90%;
    max-width: 1024px;
    margin: 20px auto 0;
}
div#footer-inner p {
    text-align: right;
    font-size: 14px;
}

/* 文章页面 */
div#article-title {
    margin: 18px 0;
    font-size: 30px;
    border-left: 6px solid #ccc;
    padding-left: 12px;
}
div#article-title a {
    color: #2a2a2a;
}
div#article-content p.picture {
    text-align: center;
}
div#article-content h1,
                    h2,
                    h3 {
    color: #2a2a2a;
}
div#article-content blockquote {
    margin-left: 5px;
    padding-left: 30px;
    border-left: 5px solid #ccc;
    font-size: 14px;
}
div#article-content li {
    margin: 7px 0;
    color: #5a5a5a;
}
div#article-meta {
    color: #999;
    padding: 6px 0;
}
div.article-block {
    border-top: 1px dashed #ccc;
}
div.article-block p.title {
    font-size: 24px;
}
div.article-block p.title a {
    color:#333;
}
div.article-block p.meta {
    text-align: right;
    color: #999;
}
div.article-block a.tag {
    margin-right: 6px;
    background-color: #999;
    color: #fff;
    padding: 3px;
    font-size: 14px;
}
div#aboutme {
    margin: 20px auto;
}
div#aboutme p {
    font-size: 14px;
    color: #666;
}
div#rss {
    border-bottom: 1px dashed #ccc;
    margin: 20px auto;
    text-align: right;
}

/* 标签配置 */
div#tag-cloud {
    border: 1px dashed #ccc;
    margin: 20px 0;
    background-color: #f0f0f0;
}
div#tag-cloud a {
    margin: 6px;
    display: block;
    float: left;
    background-color: #2a3440;
    color: #fff;
    padding: 3px;
    font-size: 14px;
    line-height: 1.2em;
}
div#tag-cloud a .count {
    color: rgba(255,255,255,.5);
    font-size: 12px;
}
div#tag-index h1 {
    border-bottom: 1px dashed #ccc;
}
div#tag-index h1 a {
    color: #2a2a2a;
    font-size: 24px;
}
div#article-tags {
    margin-top: 6px;
}
div#article-tags a.tag {
    margin-right: 6px;
    background-color: #999;
    color: #FFFFFF;
    padding: 3px;
    font-size: 14px;
}

/* 导航栏 */
div#topnav ul {
    margin: 0;
    padding: 0;
    margin-top: 18px;
}
div#topnav ul li {
    list-style: none;
    float: left;
}
div#topnav ul li a {
    display: block;
    color: #2a3440;
}
div#topnav ul li.sep {
    color: #ccc;
    margin: 0 12px;
}


================================================
FILE: startup/assets/vendor/normalize.css
================================================
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */

/* ==========================================================================
   HTML5 display definitions
   ========================================================================== */

/*
 * Corrects `block` display not defined in IE 8/9.
 */

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
    display: block;
}

/*
 * Corrects `inline-block` display not defined in IE 8/9.
 */

audio,
canvas,
video {
    display: inline-block;
}

/*
 * Prevents modern browsers from displaying `audio` without controls.
 * Remove excess height in iOS 5 devices.
 */

audio:not([controls]) {
    display: none;
    height: 0;
}

/*
 * Addresses styling for `hidden` attribute not present in IE 8/9.
 */

[hidden] {
    display: none;
}

/* ==========================================================================
   Base
   ========================================================================== */

/*
 * 1. Sets default font family to sans-serif.
 * 2. Prevents iOS text size adjust after orientation change, without disabling
 *    user zoom.
 */

html {
    font-family: sans-serif; /* 1 */
    -webkit-text-size-adjust: 100%; /* 2 */
    -ms-text-size-adjust: 100%; /* 2 */
}

/*
 * Removes default margin.
 */

body {
    margin: 0;
}

/* ==========================================================================
   Links
   ========================================================================== */

/*
 * Addresses `outline` inconsistency between Chrome and other browsers.
 */

a:focus {
    outline: thin dotted;
}

/*
 * Improves readability when focused and also mouse hovered in all browsers.
 */

a:active,
a:hover {
    outline: 0;
}

/* ==========================================================================
   Typography
   ========================================================================== */

/*
 * Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
 * Safari 5, and Chrome.
 */

h1 {
    font-size: 2em;
}

/*
 * Addresses styling not present in IE 8/9, Safari 5, and Chrome.
 */

abbr[title] {
    border-bottom: 1px dotted;
}

/*
 * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
 */

b,
strong {
    font-weight: bold;
}

/*
 * Addresses styling not present in Safari 5 and Chrome.
 */

dfn {
    font-style: italic;
}

/*
 * Addresses styling not present in IE 8/9.
 */

mark {
    background: #ff0;
    color: #000;
}


/*
 * Corrects font family set oddly in Safari 5 and Chrome.
 */

code,
kbd,
pre,
samp {
    font-family: monospace, serif;
    font-size: 1em;
}

/*
 * Improves readability of pre-formatted text in all browsers.
 */

pre {
    white-space: pre;
    white-space: pre-wrap;
    word-wrap: break-word;
}

/*
 * Sets consistent quote types.
 */

q {
    quotes: "\201C" "\201D" "\2018" "\2019";
}

/*
 * Addresses inconsistent and variable font size in all browsers.
 */

small {
    font-size: 80%;
}

/*
 * Prevents `sub` and `sup` affecting `line-height` in all browsers.
 */

sub,
sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
}

sup {
    top: -0.5em;
}

sub {
    bottom: -0.25em;
}

/* ==========================================================================
   Embedded content
   ========================================================================== */

/*
 * Removes border when inside `a` element in IE 8/9.
 */

img {
    border: 0;
}

/*
 * Corrects overflow displayed oddly in IE 9.
 */

svg:not(:root) {
    overflow: hidden;
}

/* ==========================================================================
   Figures
   ========================================================================== */

/*
 * Addresses margin not present in IE 8/9 and Safari 5.
 */

figure {
    margin: 0;
}

/* ==========================================================================
   Forms
   ========================================================================== */

/*
 * Define consistent border, margin, and padding.
 */

fieldset {
    border: 1px solid #c0c0c0;
    margin: 0 2px;
    padding: 0.35em 0.625em 0.75em;
}

/*
 * 1. Corrects color not being inherited in IE 8/9.
 * 2. Remove padding so people aren't caught out if they zero out fieldsets.
 */

legend {
    border: 0; /* 1 */
    padding: 0; /* 2 */
}

/*
 * 1. Corrects font family not being inherited in all browsers.
 * 2. Corrects font size not being inherited in all browsers.
 * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
 */

button,
input,
select,
textarea {
    font-family: inherit; /* 1 */
    font-size: 100%; /* 2 */
    margin: 0; /* 3 */
}

/*
 * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
 * the UA stylesheet.
 */

button,
input {
    line-height: normal;
}

/*
 * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
 *    and `video` controls.
 * 2. Corrects inability to style clickable `input` types in iOS.
 * 3. Improves usability and consistency of cursor style between image-type
 *    `input` and others.
 */

button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
    -webkit-appearance: button; /* 2 */
    cursor: pointer; /* 3 */
}

/*
 * Re-set default cursor for disabled elements.
 */

button[disabled],
input[disabled] {
    cursor: default;
}

/*
 * 1. Addresses box sizing set to `content-box` in IE 8/9.
 * 2. Removes excess padding in IE 8/9.
 */

input[type="checkbox"],
input[type="radio"] {
    box-sizing: border-box; /* 1 */
    padding: 0; /* 2 */
}

/*
 * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
 * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
 *    (include `-moz` to future-proof).
 */

input[type="search"] {
    -webkit-appearance: textfield; /* 1 */
    -moz-box-sizing: content-box;
    -webkit-box-sizing: content-box; /* 2 */
    box-sizing: content-box;
}

/*
 * Removes inner padding and search cancel button in Safari 5 and Chrome
 * on OS X.
 */

input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
}

/*
 * Removes inner padding and border in Firefox 4+.
 */

button::-moz-focus-inner,
input::-moz-focus-inner {
    border: 0;
    padding: 0;
}

/*
 * 1. Removes default vertical scrollbar in IE 8/9.
 * 2. Improves readability and alignment in all browsers.
 */

textarea {
    overflow: auto; /* 1 */
    vertical-align: top; /* 2 */
}

/* ==========================================================================
   Tables
   ========================================================================== */

/*
 * Remove most spacing between table cells.
 */

table {
    border-collapse: collapse;
    border-spacing: 0;
}


================================================
FILE: startup/assets/vendor/prettify-light.css
================================================
.com { color: #93a1a1; }
.lit { color: #195f91; }
.pun, .opn, .clo { color: #93a1a1; }
.fun { color: #dc322f; }
.str, .atv { color: #D14; }
.kwd, .prettyprint .tag { color: #1e347b; }
.typ, .atn, .dec, .var { color: teal; }
.pln { color: #48484c; }

.prettyprint {
  padding: 8px;
  background-color: #f7f7f9;
  border: 1px solid #e1e1e8;
}
.prettyprint.linenums {
  -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
     -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
          box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
}

/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
  margin: 0 0 0 33px; /* IE indents via margin-left */
}
ol.linenums li {
  padding-left: 12px;
  color: #bebec5;
  line-height: 20px;
  text-shadow: 0 1px 0 #fff;
}

================================================
FILE: startup/assets/vendor/prettify-night.css
================================================
/* Tomorrow Night Eighties Theme */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* Pretty printing styles. Used with prettify.js. */
/* SPAN elements with the classes below are added by prettyprint. */
/* plain text */
.pln {
  color: #cccccc;
}

@media screen {
  /* string content */
  .str {
    color: #99cc99;
  }

  /* a keyword */
  .kwd {
    color: #cc99cc;
  }

  /* a comment */
  .com {
    color: #999999;
  }

  /* a type name */
  .typ {
    color: #6699cc;
  }

  /* a literal value */
  .lit {
    color: #f99157;
  }

  /* punctuation */
  .pun {
    color: #cccccc;
  }

  /* lisp open bracket */
  .opn {
    color: #cccccc;
  }

  /* lisp close bracket */
  .clo {
    color: #cccccc;
  }

  /* a markup tag name */
  .tag {
    color: #f2777a;
  }

  /* a markup attribute name */
  .atn {
    color: #f99157;
  }

  /* a markup attribute value */
  .atv {
    color: #66cccc;
  }

  /* a declaration */
  .dec {
    color: #f99157;
  }

  /* a variable name */
  .var {
    color: #f2777a;
  }

  /* a function name */
  .fun {
    color: #6699cc;
  }
}
/* Use higher contrast and text-weight for printable form. */
@media print, projection {
  .str {
    color: #006600;
  }

  .kwd {
    color: #006;
    font-weight: bold;
  }

  .com {
    color: #600;
    font-style: italic;
  }

  .typ {
    color: #404;
    font-weight: bold;
  }

  .lit {
    color: #004444;
  }

  .pun, .opn, .clo {
    color: #444400;
  }

  .tag {
    color: #006;
    font-weight: bold;
  }

  .atn {
    color: #440044;
  }

  .atv {
    color: #006600;
  }
}
/* Style */
pre.prettyprint {
  background: #2d2d2d;
  font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  border: 1px solid #cccccc;
  padding: 10px;
}

/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
  margin-top: 0;
  margin-bottom: 0;
  color: grey;
}

/* IE indents via margin-left */
li.L0,
li.L1,
li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
  /* */
}

/* Alternate shading for lines */
li.L1,
li.L3,
li.L5,
li.L7,
li.L9 {
  /* */
}


================================================
FILE: startup/assets/vendor/prettify.js
================================================
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();


================================================
FILE: startup/ext.yml
================================================


================================================
FILE: startup/navbar.yml
================================================
- label: 关于我
  href: /pages/about-me.html
  target: _self

- label: github
  href: https://github.com/ericzhang-cn/papery
  target: _blank


================================================
FILE: startup/pages/about-me.md
================================================
个人介绍
=======

papery - 献给所有简洁控和极客的blog


================================================
FILE: startup/pages.yml
================================================
- id: about-me
  title: About me


================================================
FILE: startup/site.yml
================================================
title: Title of blog
subtitle: Subtitle of blog
link: Homepage link
meta:
  description: Content of description meta tag
  keywords: !!seq
    - keyword1
    - keyword2
    - keyword3
  author: Content of author meta tag
master:
  name: Your name
  about: Introduce yourself here
  email: Your E-mail
rss:
  title: RSS feed title
  desc: RSS feed description
  lang: zh-cn
  max: 10
copyright:
  owner: Copyrighter
  beginYear: 2011
  endYear: 2013
theme: default
codetheme: night


================================================
FILE: startup/templates/articles.ejs
================================================
<% include inc/header %>
<div id="main">
    <div id="main-inner">
        <div id="topnav">
            <ul>
                <li><a href="/">首页</a></li>
                <li class="sep"> | </li>
                <li><a href="/tag.html">标签</a></li>
                <% include inc/navbar %>
            </ul>
            <div style="clear:both;"></div>
        </div>
        <div id="article-title">
            <a href="<%= article.link %>"><%= article.title %></a>
        </div>
        <div id="article-meta">
            作者 <%= article.author %> | 发布于 <%= article.postedOn %>
        </div>
        <div id="article-tags">
        <% article.tags.forEach(function (tag) { %>
        <a class="tag" href="/tag.html#<%= tag %>"><%= tag %></a>
        <% }); %>
        </div>
        <div id="article-content">
            <%- article.content %>
        </div>
        <% include inc/article_footer_plugin %>
    </div>
</div>
<% include inc/footer %>


================================================
FILE: startup/templates/inc/article_footer_plugin.ejs
================================================


================================================
FILE: startup/templates/inc/footer.ejs
================================================
        <div id="footer">
            <div id="footer-inner">
                <p id="copyright">Copyright (c) <%= site.copyright.beginYear %>-<%= site.copyright.endYear %> <%- site.copyright.owner %></p>
            </div>
        </div>
        <% include footer_plugin %>
    </body>
</html>


================================================
FILE: startup/templates/inc/footer_plugin.ejs
================================================
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [['$','$'], ['\\(','\\)']],
            processEscapes: true
        }
    });
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>


================================================
FILE: startup/templates/inc/header.ejs
================================================
<!doctype html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <meta name="description" content="<%= site.meta.description %>"/>
        <meta name="keywords" content="<%= site.meta.keywords %>"/>
        <meta name="author" content="<%= site.meta.author %>"/>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
        <title><%= site.title %> - <% include subtitle %></title>
        <link rel="stylesheet" href="/assets/vendor/normalize.css"/>
        <link rel="stylesheet" href="/assets/vendor/prettify-<%= site.codetheme %>.css"/>
        <link rel="stylesheet" href="/assets/themes/<%= site.theme %>/main.css"/>
        <link rel="shortcut icon" href="/fav.ico"/>
        <script type="text/javascript" src="/assets/vendor/prettify.js"></script>
        <% include header_plugin %>
    </head>
    <body onload="prettyPrint()">
    <div id="header">
        <div id="header-inner">
            <div id="title"><a href="/"><%= site.title %></a></div>
            <div id="subtitle"><%= site.subtitle %></div>
        </div>
    </div>


================================================
FILE: startup/templates/inc/header_plugin.ejs
================================================


================================================
FILE: startup/templates/inc/navbar.ejs
================================================
<% navbar.forEach(function (nav) { %>
<li class="sep"> | </li>
<li><a href="<%= nav.href %>" target="<%= nav.target %>"><%= nav.label %></a></li>
<% }) %>


================================================
FILE: startup/templates/inc/subtitle.ejs
================================================
<% if (typeof article !== 'undefined') { %><%= article.title %><% } else if (typeof page !== 'undefined') { %><%= page.title %><% } else { %><%= site.subtitle %><% } %>


================================================
FILE: startup/templates/index.ejs
================================================
<% include inc/header %>
<div id="main">
    <div id="main-inner">
        <div id="topnav">
            <ul>
                <li><a href="/">首页</a></li>
                <li class="sep"> | </li>
                <li><a href="/tag.html">标签</a></li>
                <% include inc/navbar %>
            </ul>
            <div style="clear:both;"></div>
        </div>
        <div id="aboutme"><p><%- site.master.about %></p></div>
        <div id="tag-cloud">
            <% coreTags.forEach(function (tag) { %>
            <a href="/tag.html#<%= tag.label %>"><%= tag.label %><span class="count">×<%= tag.count %></span></a>
            <% }) %>
            <div style="clear:both;"></div>
        </div>
        <div id="article-blocks">
            <% articles.forEach(function (ar) { %>
            <div class="article-block">
                <p class="title"><a href="/articles/<%= ar.id %>.html"><%= ar.title %></a></p>
                <p class="abstract">&lt;摘要&gt;: <%= ar.abstract %></p>
                <p class="meta">作者 <%= ar.author %> | 发布于 <%= ar.postedOn %> | Tags 
                    <% ar.tags.forEach(function (tag) { %>
                    <a class="tag" href="/tag.html#<%= tag %>"><%= tag %></a>
                    <% }) %>
                </p>
            </div>
            <% }) %>
        </div>
    </div>
</div>
<% include inc/footer %>


================================================
FILE: startup/templates/pages.ejs
================================================
<% include inc/header %>
<div id="main">
    <div id="main-inner">
        <div id="topnav">
            <ul>
                <li><a href="/">首页</a></li>
                <li class="sep"> | </li>
                <li><a href="/tag.html">标签</a></li>
                <% include inc/navbar %>
            </ul>
            <div style="clear:both;"></div>
        </div>
        <h1><%= page.title %></h1>
        <div id="page-content"><%- page.content %></div>
    </div>
</div>
<% include inc/footer %>


================================================
FILE: startup/templates/rss.ejs
================================================
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
        <title><%= site.rss.title %></title>
        <link><%= site.link %></link>
        <description><%= site.rss.desc %></description>
        <lastBuildDate><%= site.rss.lastBuildDate %></lastBuildDate>
        <language><%= site.rss.lang %></language>
        <% site.rss.items.forEach(function (item) { %>
        <item>
            <title><%= item.title %></title>
            <link><%= item.link %></link>
            <guid><%= item.guid %></guid>
            <author><%= item.author %></author>
            <pubDate><%= item.pubDate %></pubDate>
            <description><%= item.description %></description>
        </item>
        <% }) %>
    </channel>
</rss>


================================================
FILE: startup/templates/tag.ejs
================================================
<% include inc/header %>
<div id="main">
    <div id="main-inner">
        <div id="topnav">
            <ul>
                <li><a href="/">首页</a></li>
                <li class="sep"> | </li>
                <li><a href="/tag.html">标签</a></li>
                <% include inc/navbar %>
            </ul>
            <div style="clear:both;"></div>
        </div>
        <div id="tag-cloud">
        <% tags.forEach(function (tag) { %>
        <a href="/tag.html#<%= tag.label %>"><%= tag.label %><span class="count">×<%= tag.count %></span></a>
        <% }) %>
        <div style="clear:both;"></div></div>
        <div id="tag-index">
        <% tags.forEach(function (tag) { %>
            <h1><a name="<%= tag.label %>"><%= tag.label %></a></h1>
            <% tag.articles.forEach(function (ar) { %>
            <p><a href="/articles/<%= ar.id %>.html"><%= ar.title %></a></p>
            <% }) %>
        <% }) %>
        </div>
    </div>
</div>
<% include inc/footer %>


================================================
FILE: test/tests.js
================================================
var fs = require('fs');

var fse = require('fs-extra');
var should = require('chai').should();

var createRunner = require('../bin/create_runner');
var buildRunner = require('../bin/build_runner');
var serverRunner = require('../bin/server_runner');

var ex = fs.existsSync;

describe('Papery', function () {
    var tmpDir = './_test_tmp';
    var blogDir = tmpDir + '/blog';

    before(function () {
        if (fs.existsSync(tmpDir)) {
            fse.removeSync(tmpDir);
        }
        fs.mkdir(tmpDir);
    });

    describe('Create', function () {
        before(function () {
            createRunner.run([ blogDir ]);
        });

        it('should create root directory', function () {
            ex(blogDir).should.to.be.true;
        });
        it('should create sub-directories', function () {
            ex(blogDir + '/articles').should.to.be.true;
            ex(blogDir + '/assets').should.to.be.true;
            ex(blogDir + '/pages').should.to.be.true;
            ex(blogDir + '/templates').should.to.be.true;
        });
        it('should create yaml config files', function () {
            ex(blogDir + '/articles.yml').should.to.be.true;
            ex(blogDir + '/ext.yml').should.to.be.true;
            ex(blogDir + '/navbar.yml').should.to.be.true;
            ex(blogDir + '/pages.yml').should.to.be.true;
            ex(blogDir + '/site.yml').should.to.be.true;
        });
    });

    describe('Build', function () {
        before(function () {
            buildRunner.run([ blogDir ]);
        });

        it('should build index page', function () {
            ex(blogDir + '/index.html').should.to.be.true;
        });
        it('should build tag page', function () {
            ex(blogDir + '/tag.html').should.to.be.true;
        });
        it('should build RSS feed', function () {
            ex(blogDir + '/rss.xml').should.to.be.true;
        });
        it('should build articles', function () {
            ex(blogDir + '/articles/papery-quickstart.html').should.to.be.true;
            ex(blogDir + '/articles/code-and-math.html').should.to.be.true;
        });
        it('should build pages', function () {
            ex(blogDir + '/pages/about-me.html').should.to.be.true;
        });
    });

    after(function() {
        if (fs.existsSync(tmpDir)) {
            fse.removeSync(tmpDir);
        }
    });
});
Download .txt
gitextract_td6nmx1z/

├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bin/
│   ├── build_runner.js
│   ├── create_runner.js
│   ├── papery.js
│   └── server_runner.js
├── lib/
│   ├── compiler.js
│   └── parser.js
├── package.json
├── startup/
│   ├── articles/
│   │   ├── code-and-math.md
│   │   └── papery-quickstart.md
│   ├── articles.yml
│   ├── assets/
│   │   ├── themes/
│   │   │   └── default/
│   │   │       └── main.css
│   │   └── vendor/
│   │       ├── normalize.css
│   │       ├── prettify-light.css
│   │       ├── prettify-night.css
│   │       └── prettify.js
│   ├── ext.yml
│   ├── navbar.yml
│   ├── pages/
│   │   └── about-me.md
│   ├── pages.yml
│   ├── site.yml
│   └── templates/
│       ├── articles.ejs
│       ├── inc/
│       │   ├── article_footer_plugin.ejs
│       │   ├── footer.ejs
│       │   ├── footer_plugin.ejs
│       │   ├── header.ejs
│       │   ├── header_plugin.ejs
│       │   ├── navbar.ejs
│       │   └── subtitle.ejs
│       ├── index.ejs
│       ├── pages.ejs
│       ├── rss.ejs
│       └── tag.ejs
└── test/
    └── tests.js
Download .txt
SYMBOL INDEX (13 symbols across 4 files)

FILE: bin/build_runner.js
  function _help (line 8) | function _help() {

FILE: bin/create_runner.js
  function _help (line 11) | function _help() {

FILE: bin/server_runner.js
  function _help (line 10) | function _help () {

FILE: startup/assets/vendor/prettify.js
  function L (line 2) | function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var...
  function M (line 6) | function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.classN...
  function B (line 7) | function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}
  function x (line 7) | function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(...
  function u (line 9) | function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''...
  function D (line 12) | function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.clas...
  function k (line 15) | function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(...
  function C (line 15) | function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-m...
  function E (line 15) | function E(a){var m=
  function m (line 25) | function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Inf...
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (82K chars).
[
  {
    "path": ".gitignore",
    "chars": 33,
    "preview": "node_modules\nnpm-debug.log\n.idea\n"
  },
  {
    "path": ".travis.yml",
    "chars": 49,
    "preview": "language: node_js\nnode_js:\n  - \"0.11\"\n  - \"0.10\"\n"
  },
  {
    "path": "LICENSE",
    "chars": 1054,
    "preview": "Copyright (c) 2013 Eric Zhang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this soft"
  },
  {
    "path": "README.md",
    "chars": 7454,
    "preview": "[![Build Status](https://travis-ci.org/ericzhang-cn/papery.png)](https://travis-ci.org/ericzhang-cn/papery)\n\nPapery - Cr"
  },
  {
    "path": "bin/build_runner.js",
    "chars": 456,
    "preview": "/**\n * 编译整个站点\n */\n\nvar _ = require('underscore');\nvar compiler = require('../lib/compiler');\n\nfunction _help() {\n    con"
  },
  {
    "path": "bin/create_runner.js",
    "chars": 846,
    "preview": "/**\n * 生成一个新的papery站点\n */\n\nvar _ = require('underscore');\nvar fs = require('fs');\nvar fse = require('fs-extra');\nvar log"
  },
  {
    "path": "bin/papery.js",
    "chars": 1862,
    "preview": "#!/usr/bin/env node\n\n/**\n * 程序入口\n */\n\nvar _ = require('underscore');\n\nvar cmds = [ 'create', 'build', 'server' ];\nvar ru"
  },
  {
    "path": "bin/server_runner.js",
    "chars": 973,
    "preview": "/**\n * 在本地启动一个调试服务器\n */\n\nvar _ = require('underscore');\nvar connect = require('connect');\nvar log4js = require('log4js')"
  },
  {
    "path": "lib/compiler.js",
    "chars": 6262,
    "preview": "/**\n * 生成编译后的静态页面文件\n * @module lib/compiler\n */\n\nvar fs = require('fs');\nvar ejs = require('ejs');\nvar marked = require("
  },
  {
    "path": "lib/parser.js",
    "chars": 2722,
    "preview": "/**\n * 解析YAML配置文件信息\n * @module lib/parser\n */\n\nvar _ = require('underscore');\nvar fs = require('fs');\nvar moment = requi"
  },
  {
    "path": "package.json",
    "chars": 762,
    "preview": "{\n  \"name\": \"papery\",\n  \"version\": \"0.2.5-2\",\n  \"description\": \"create your simple, fast & elegant blog with plain text\""
  },
  {
    "path": "startup/articles/code-and-math.md",
    "chars": 440,
    "preview": "这是一段代码\n===========\n\n```c\n#include <stdio.h>\n\nint main(int argc, char** argv) {\n    printf(\"%s\\n\", \"Hello, World!\");\n}\n``"
  },
  {
    "path": "startup/articles/papery-quickstart.md",
    "chars": 7201,
    "preview": "Papery - create your simple, fast & elegant blog with plain text.\n\n# 使用说明\n## 安装及升级\n首先要保证机器上安装有[NodeJS](http://nodejs.org"
  },
  {
    "path": "startup/articles.yml",
    "chars": 306,
    "preview": "- id: papery-quickstart\n  title: Papery快速指南\n  postedOn: !!str 2013-01-02\n  author: Papery\n  tags: \n    - Papery\n    - 指南"
  },
  {
    "path": "startup/assets/themes/default/main.css",
    "chars": 3435,
    "preview": "/* 基础而通用的样式 */\nbody {\n    font-family: \"Helvetica Neue\", Helvetica, Arial, \"Hiragino Sans GB\", \"Hiragino Sans GB W3\", \"W"
  },
  {
    "path": "startup/assets/vendor/normalize.css",
    "chars": 6875,
    "preview": "/*! normalize.css v2.0.1 | MIT License | git.io/normalize */\n\n/* ======================================================="
  },
  {
    "path": "startup/assets/vendor/prettify-light.css",
    "chars": 817,
    "preview": ".com { color: #93a1a1; }\n.lit { color: #195f91; }\n.pun, .opn, .clo { color: #93a1a1; }\n.fun { color: #dc322f; }\n.str, .a"
  },
  {
    "path": "startup/assets/vendor/prettify-night.css",
    "chars": 2156,
    "preview": "/* Tomorrow Night Eighties Theme */\n/* Original theme - https://github.com/chriskempson/tomorrow-theme */\n/* Pretty prin"
  },
  {
    "path": "startup/assets/vendor/prettify.js",
    "chars": 13632,
    "preview": "var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;\n(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92"
  },
  {
    "path": "startup/ext.yml",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "startup/navbar.yml",
    "chars": 139,
    "preview": "- label: 关于我\n  href: /pages/about-me.html\n  target: _self\n\n- label: github\n  href: https://github.com/ericzhang-cn/paper"
  },
  {
    "path": "startup/pages/about-me.md",
    "chars": 39,
    "preview": "个人介绍\n=======\n\npapery - 献给所有简洁控和极客的blog\n"
  },
  {
    "path": "startup/pages.yml",
    "chars": 33,
    "preview": "- id: about-me\n  title: About me\n"
  },
  {
    "path": "startup/site.yml",
    "chars": 481,
    "preview": "title: Title of blog\nsubtitle: Subtitle of blog\nlink: Homepage link\nmeta:\n  description: Content of description meta tag"
  },
  {
    "path": "startup/templates/articles.ejs",
    "chars": 953,
    "preview": "<% include inc/header %>\n<div id=\"main\">\n    <div id=\"main-inner\">\n        <div id=\"topnav\">\n            <ul>\n          "
  },
  {
    "path": "startup/templates/inc/article_footer_plugin.ejs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "startup/templates/inc/footer.ejs",
    "chars": 294,
    "preview": "        <div id=\"footer\">\n            <div id=\"footer-inner\">\n                <p id=\"copyright\">Copyright (c) <%= site.c"
  },
  {
    "path": "startup/templates/inc/footer_plugin.ejs",
    "chars": 321,
    "preview": "<script type=\"text/x-mathjax-config\">\n    MathJax.Hub.Config({\n        tex2jax: {\n            inlineMath: [['$','$'], ['"
  },
  {
    "path": "startup/templates/inc/header.ejs",
    "chars": 1213,
    "preview": "<!doctype html>\n<html>\n    <head>\n        <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\"/>\n        <meta "
  },
  {
    "path": "startup/templates/inc/header_plugin.ejs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "startup/templates/inc/navbar.ejs",
    "chars": 155,
    "preview": "<% navbar.forEach(function (nav) { %>\n<li class=\"sep\"> | </li>\n<li><a href=\"<%= nav.href %>\" target=\"<%= nav.target %>\">"
  },
  {
    "path": "startup/templates/inc/subtitle.ejs",
    "chars": 169,
    "preview": "<% if (typeof article !== 'undefined') { %><%= article.title %><% } else if (typeof page !== 'undefined') { %><%= page.t"
  },
  {
    "path": "startup/templates/index.ejs",
    "chars": 1365,
    "preview": "<% include inc/header %>\n<div id=\"main\">\n    <div id=\"main-inner\">\n        <div id=\"topnav\">\n            <ul>\n          "
  },
  {
    "path": "startup/templates/pages.ejs",
    "chars": 500,
    "preview": "<% include inc/header %>\n<div id=\"main\">\n    <div id=\"main-inner\">\n        <div id=\"topnav\">\n            <ul>\n          "
  },
  {
    "path": "startup/templates/rss.ejs",
    "chars": 747,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\">\n    <channel>\n        <title><%= site.rss.title %></title>\n  "
  },
  {
    "path": "startup/templates/tag.ejs",
    "chars": 981,
    "preview": "<% include inc/header %>\n<div id=\"main\">\n    <div id=\"main-inner\">\n        <div id=\"topnav\">\n            <ul>\n          "
  },
  {
    "path": "test/tests.js",
    "chars": 2373,
    "preview": "var fs = require('fs');\n\nvar fse = require('fs-extra');\nvar should = require('chai').should();\n\nvar createRunner = requi"
  }
]

About this extraction

This page contains the full source code of the ericzhang-cn/papery GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (65.5 KB), approximately 23.4k tokens, and a symbol index with 13 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!