├── .gitignore ├── test ├── avatar.jpg └── singup.js ├── public └── css │ ├── themes │ ├── basic │ │ └── assets │ │ │ └── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.ttf │ │ │ └── icons.woff │ ├── default │ │ └── assets │ │ │ ├── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ └── icons.woff2 │ │ │ └── images │ │ │ └── flags.png │ ├── github │ │ └── assets │ │ │ └── fonts │ │ │ ├── octicons.ttf │ │ │ ├── octicons.woff │ │ │ └── octicons-local.ttf │ └── material │ │ └── assets │ │ └── fonts │ │ ├── icons.eot │ │ ├── icons.ttf │ │ └── icons.woff │ ├── floder.css │ ├── components │ ├── sticky.min.css │ ├── tab.min.css │ ├── breadcrumb.min.css │ ├── nag.min.css │ ├── rail.min.css │ ├── sticky.css │ ├── site.min.css │ ├── embed.min.css │ ├── container.min.css │ ├── ad.min.css │ ├── tab.css │ ├── reset.min.css │ ├── shape.min.css │ ├── dimmer.min.css │ ├── breadcrumb.css │ ├── comment.min.css │ ├── nag.css │ ├── rail.css │ ├── site.css │ ├── feed.min.css │ ├── image.min.css │ ├── container.css │ ├── embed.css │ ├── shape.css │ ├── loader.min.css │ ├── dimmer.css │ ├── ad.css │ ├── rating.min.js │ ├── reveal.min.css │ ├── item.min.css │ ├── nag.min.js │ ├── divider.min.css │ ├── search.min.css │ ├── site.min.js │ ├── comment.css │ ├── modal.min.css │ ├── message.min.css │ ├── accordion.min.css │ ├── image.css │ ├── accordion.min.js │ ├── feed.css │ └── progress.min.css │ └── style.css ├── views ├── 404.html ├── error.html ├── signin.html ├── create.html ├── components │ ├── header.html │ └── comments.html ├── edit.html ├── signup.html ├── posts.html ├── base.html └── post.html ├── config └── default.js ├── models ├── users.js ├── comments.js └── posts.js ├── routes ├── signout.js ├── index.js ├── signin.js ├── signup.js └── posts.js ├── README.md ├── middlewares └── check.js ├── logger.js ├── template.js ├── package.json ├── package_back.json ├── lib └── mongo.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | coverage 4 | -------------------------------------------------------------------------------- /test/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/test/avatar.jpg -------------------------------------------------------------------------------- /public/css/themes/basic/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/basic/assets/fonts/icons.eot -------------------------------------------------------------------------------- /public/css/themes/basic/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/basic/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /public/css/themes/basic/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/basic/assets/fonts/icons.woff -------------------------------------------------------------------------------- /public/css/themes/default/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/default/assets/fonts/icons.eot -------------------------------------------------------------------------------- /public/css/themes/default/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/default/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /public/css/themes/default/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/default/assets/fonts/icons.woff -------------------------------------------------------------------------------- /public/css/themes/default/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/default/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /public/css/themes/default/assets/images/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/default/assets/images/flags.png -------------------------------------------------------------------------------- /public/css/themes/github/assets/fonts/octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/github/assets/fonts/octicons.ttf -------------------------------------------------------------------------------- /public/css/themes/github/assets/fonts/octicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/github/assets/fonts/octicons.woff -------------------------------------------------------------------------------- /public/css/themes/material/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/material/assets/fonts/icons.eot -------------------------------------------------------------------------------- /public/css/themes/material/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/material/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /public/css/themes/material/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/material/assets/fonts/icons.woff -------------------------------------------------------------------------------- /public/css/themes/github/assets/fonts/octicons-local.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/995270418L/koa2-blog/HEAD/public/css/themes/github/assets/fonts/octicons-local.ttf -------------------------------------------------------------------------------- /views/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /views/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | 7 | 8 | 9 |

{{ error.message}}

10 |

{{ error.stack }}

11 | 12 | -------------------------------------------------------------------------------- /config/default.js: -------------------------------------------------------------------------------- 1 | let config = { 2 | port: 3000, 3 | mongodb: 'mongodb://127.0.0.1:27017/f_blog', 4 | redis_host:'127.0.0.1', 5 | redis_port: 6379, 6 | redis_db:0, 7 | redis_password:'', 8 | ENV : 'development', 9 | blog_title: 'Koa2-blog', 10 | blog_description: '让我们一起,为梦想窒息!' 11 | }; 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /models/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 用户模块(crud) 3 | */ 4 | const User = require('../lib/mongo').User; 5 | 6 | module.exports = { 7 | create : function create(user){ 8 | return User.create(user).exec(); 9 | }, 10 | getUserByName: function getUserByName(name){ 11 | return User.findOne({name:name}) 12 | .addCreatedAt() //自定义插件(通过_id生成时间戳) 13 | .exec(); 14 | } 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /routes/signout.js: -------------------------------------------------------------------------------- 1 | //处理所有以 '/signout' 开头的请求 2 | const koaRouter = require('koa-router'); 3 | const checkLogin = require('../middlewares/check').checkLogin; 4 | 5 | const router = new koaRouter({ 6 | prefix: '/signout' 7 | }); 8 | 9 | //GET /signout 登出 10 | router.get('/',checkLogin,async (ctx,next) =>{ 11 | //清空session信息 12 | ctx.session.user =null; 13 | ctx.flash('success','登出成功'); 14 | ctx.redirect('/posts'); 15 | }); 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /public/css/floder.css: -------------------------------------------------------------------------------- 1 | body,button,input,select,testarea{ 2 | font-weight: 300; 3 | font-family:"Open Sans",Tahoma,arial,"Hiragino Sans GB","Hiragino Sans GB W3",STHeiti,"Microsoft YaHei",sans-serif 4 | } 5 | 6 | body{ 7 | font-size: 18px; 8 | color: #222; 9 | } 10 | 11 | 12 | .page_header_bar{ 13 | position: fixed; 14 | left:0; 15 | top:0; 16 | width:100%; 17 | height:60px; 18 | z-index: 100; //堆叠顺序,也就是离用户的距离 19 | transition: background 1s linear; 20 | background: rgba(255,255,255,.2); 21 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # koa2-blog 2 |   该项目基于node-v7.7.1,mongodb v3.4.2,redis 3.2.8构建而成,最主要的是node的版本一定要支持async/await语法,否则只能使用babel了。 3 | 4 |   项目本地使用: 5 |   1. 开启mongod服务: 6 | 7 | -> mongod --fork --logpath ~/data/log/mongodb.log --dbpath ~/data/db 8 |   2.开启redis-server 9 | 10 | -> redis-server & 11 |   3.运行项目: 12 | 13 | -> node index.js 14 |   项目使用[redis](https://redis.io/)做session管理,[Koa2](http://koajs.com/)做系统框架,[mondogb](https://www.mongodb.com/cn)做储存,flash做消息中间件。具体请看[源代码](https://github.com/995270418L/koa2-blog/),注释都写的非常清楚。大家不懂的多查查文档就可以了,也可以留言一同解决。 15 | -------------------------------------------------------------------------------- /middlewares/check.js: -------------------------------------------------------------------------------- 1 | 2 | //给每个路由都使用这个middleware检查用户权限 3 | 4 | module.exports = { 5 | checkLogin : async (ctx,next) =>{ 6 | if(!ctx.session.user){ 7 | //将验证消息添加到redis message(flash-message) 8 | ctx.flash('error',"未登录"); 9 | //请求重定向 10 | return ctx.redirect('/signin'); 11 | } 12 | await next(); 13 | }, 14 | checkNotLogin: async (ctx,next)=>{ 15 | if(ctx.session.user){ 16 | ctx.flash('success',"已登录"); 17 | return ctx.redirect('back'); //返回之前的页面 18 | } 19 | await next(); 20 | } 21 | }; -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | //获取所有路由 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const koaRouter = require('koa-router'); 5 | const router = new koaRouter(); 6 | //路由管理文件(处理一类请求) 7 | fs 8 | .readdirSync(__dirname) 9 | .filter(file => (file.indexOf('.') !==0) && (file.split('.').slice(-1)[0] === 'js') && file !== 'index.js') 10 | .forEach(file => { 11 | const route = require(path.join(__dirname,file)); 12 | router.use(route.routes(),route.allowedMethods()); 13 | }); 14 | 15 | router.get('/', async (ctx,next) => { 16 | ctx.redirect('/posts'); 17 | }); 18 | 19 | module.exports = router; -------------------------------------------------------------------------------- /public/css/components/sticky.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Sticky 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.sticky{position:static;-webkit-transition:none;transition:none;z-index:800}.ui.sticky.bound{position:absolute;left:auto;right:auto}.ui.sticky.fixed{position:fixed;left:auto;right:auto}.ui.sticky.bound.top,.ui.sticky.fixed.top{top:0;bottom:auto}.ui.sticky.bound.bottom,.ui.sticky.fixed.bottom{top:auto;bottom:0}.ui.native.sticky{position:-webkit-sticky;position:-moz-sticky;position:-ms-sticky;position:-o-sticky;position:sticky} -------------------------------------------------------------------------------- /views/signin.html: -------------------------------------------------------------------------------- 1 | {% extends './components/header.html'%}{% block main %} 2 | 3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 |
17 |
18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /logger.js: -------------------------------------------------------------------------------- 1 | //日志传输流 2 | const winston = require('winston'); 3 | 4 | let successLogger = new (winston.Logger)({ 5 | transports:[ 6 | new winston.transports.Console({ 7 | name:'console-success-log', 8 | level: 'info', 9 | json:true, 10 | colorize: true 11 | }), 12 | new (winston.transports.File)({ 13 | name: 'file-info-log', 14 | filename: 'logs/success.log' 15 | }) 16 | ] 17 | }); 18 | 19 | let errorLogger = new (winston.Logger)({ 20 | transport:[ 21 | new winston.transports.Console({ 22 | name: 'console-error-log', 23 | json: true, 24 | colorize: true 25 | }), 26 | new winston.transports.File({ 27 | name: 'file-error-log', 28 | filename: 'logs/error.log' 29 | }) 30 | ] 31 | }); 32 | 33 | module.exports = { 34 | success : successLogger, 35 | error : errorLogger 36 | } 37 | -------------------------------------------------------------------------------- /public/css/components/tab.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Tab 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.tab{display:none}.ui.tab.active,.ui.tab.open{display:block}.ui.tab.loading{position:relative;overflow:hidden;display:block;min-height:250px}.ui.tab.loading *{position:relative!important;left:-10000px!important}.ui.tab.loading.segment:before,.ui.tab.loading:before{position:absolute;content:'';top:100px;left:50%;margin:-1.25em 0 0 -1.25em;width:2.5em;height:2.5em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.tab.loading.segment:after,.ui.tab.loading:after{position:absolute;content:'';top:100px;left:50%;margin:-1.25em 0 0 -1.25em;width:2.5em;height:2.5em;-webkit-animation:button-spin .6s linear;animation:button-spin .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#767676 transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent} -------------------------------------------------------------------------------- /template.js: -------------------------------------------------------------------------------- 1 | //初始化nunjucks 2 | const views = require('koa-nunjucks-next'); 3 | const config = require('./config/default'); 4 | 5 | function template(path,opts){ 6 | let autoescape = opts.autoescape || true; //是否编译输出 7 | let watch = opts.watch || false; //是否映射模板更改 8 | let noCache = opts.noCache || false; //默认需要缓存 9 | // let globals = opts.globals; 10 | // console.log(globals); 11 | return views(path,{ 12 | autoescape:autoescape, 13 | watch:watch, 14 | noCache:noCache, 15 | globals :{ 16 | blog_title: config.blog_title, 17 | blog_description : config.blog_description 18 | }, 19 | filters: { 20 | asyncAdd: (val1, val2) => { 21 | return new Promise((resolve, reject) => { 22 | setTimeout(() => resolve(val1 + val2), 2000); 23 | }); 24 | }, 25 | syncAdd: (val1, val2) => { 26 | return val1 + val2; 27 | } 28 | } 29 | }); 30 | }; 31 | 32 | //输出整合好的模板 33 | module.exports = template; 34 | 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "f-blog", 3 | "version": "1.0.0", 4 | "description": "'floder's blog'", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "node --harmony ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha" 8 | }, 9 | "author": "floder", 10 | "license": "ISC", 11 | "dependencies": { 12 | "koa": "^2.0.1-alpha.8", 13 | "koa-bodyparser": "^3.2.0", 14 | "koa-flash2": "0.0.2", 15 | "koa-generic-session": "^1.11.5", 16 | "koa-logger-winston": "0.0.2", 17 | "koa-multer": "^1.0.1", 18 | "koa-nunjucks-next": "^1.1.2", 19 | "koa-redis": "^2.1.3", 20 | "koa-router": "^7.0.1", 21 | "koa-session2": "^2.2.1", 22 | "koa-static": "^3.0.0", 23 | "markdown": "^0.5.0", 24 | "marked": "^0.3.6", 25 | "mocha": "^3.2.0", 26 | "moment": "^2.17.1", 27 | "mongolass": "^2.4.2", 28 | "objectid-to-timestamp": "^1.3.0", 29 | "redis": "^2.6.5", 30 | "semantic-ui": "^2.2.9", 31 | "supertest": "^3.0.0", 32 | "winston": "^2.3.1" 33 | }, 34 | "devDependencies": { 35 | "istanbul": "^0.4.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /views/create.html: -------------------------------------------------------------------------------- 1 | {% extends './components/header.html' %}{% block main %} 2 | 3 |
4 |
5 | 6 | 7 | 8 |
9 |
10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 | 25 | 29 | {% endblock %} -------------------------------------------------------------------------------- /public/css/components/breadcrumb.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Breadcrumb 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.breadcrumb{line-height:1;display:inline-block;margin:0 0;vertical-align:middle}.ui.breadcrumb:first-child{margin-top:0}.ui.breadcrumb:last-child{margin-bottom:0}.ui.breadcrumb .divider{display:inline-block;opacity:.7;margin:0 .21428571rem 0;font-size:.92857143em;color:rgba(0,0,0,.4);vertical-align:baseline}.ui.breadcrumb a{color:#4183c4}.ui.breadcrumb a:hover{color:#1e70bf}.ui.breadcrumb .icon.divider{font-size:.85714286em;vertical-align:baseline}.ui.breadcrumb a.section{cursor:pointer}.ui.breadcrumb .section{display:inline-block;margin:0;padding:0}.ui.breadcrumb.segment{display:inline-block;padding:.78571429em 1em}.ui.breadcrumb .active.section{font-weight:700}.ui.mini.breadcrumb{font-size:.78571429rem}.ui.tiny.breadcrumb{font-size:.85714286rem}.ui.small.breadcrumb{font-size:.92857143rem}.ui.breadcrumb{font-size:1rem}.ui.large.breadcrumb{font-size:1.14285714rem}.ui.big.breadcrumb{font-size:1.28571429rem}.ui.huge.breadcrumb{font-size:1.42857143rem}.ui.massive.breadcrumb{font-size:1.71428571rem} -------------------------------------------------------------------------------- /views/components/header.html: -------------------------------------------------------------------------------- 1 | {% extends '../base.html' %} {% block body %} 2 | 28 |
29 |
30 |
31 |
32 | {% if success %} 33 |
34 |

{{ success }}

35 |
36 | {% elif error %} 37 |
38 |

{{ error }}

39 |
40 | {% endif %} 41 |
42 |
43 | {% block main %}{% endblock %} 44 | {% endblock %} -------------------------------------------------------------------------------- /views/edit.html: -------------------------------------------------------------------------------- 1 | {% extends './components/header.html' %}{% block main %} 2 |
3 |
4 | 8 | 9 | 10 |
11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 | 27 | 31 | {% endblock %} -------------------------------------------------------------------------------- /public/css/components/nag.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Nag 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.nag{display:none;opacity:.95;position:relative;top:0;left:0;z-index:999;min-height:0;width:100%;margin:0;padding:.75em 1em;background:#555;box-shadow:0 1px 2px 0 rgba(0,0,0,.2);font-size:1rem;text-align:center;color:rgba(0,0,0,.87);border-radius:0 0 .28571429rem .28571429rem;-webkit-transition:.2s background ease;transition:.2s background ease}a.ui.nag{cursor:pointer}.ui.nag>.title{display:inline-block;margin:0 .5em;color:#fff}.ui.nag>.close.icon{cursor:pointer;opacity:.4;position:absolute;top:50%;right:1em;font-size:1em;margin:-.5em 0 0;color:#fff;-webkit-transition:opacity .2s ease;transition:opacity .2s ease}.ui.nag:hover{background:#555;opacity:1}.ui.nag .close:hover{opacity:1}.ui.overlay.nag{position:absolute;display:block}.ui.fixed.nag{position:fixed}.ui.bottom.nag,.ui.bottom.nags{border-radius:.28571429rem .28571429rem 0 0;top:auto;bottom:0}.ui.inverted.nag,.ui.inverted.nags .nag{background-color:#f3f4f5;color:rgba(0,0,0,.85)}.ui.inverted.nag .close,.ui.inverted.nag .title,.ui.inverted.nags .nag .close,.ui.inverted.nags .nag .title{color:rgba(0,0,0,.4)}.ui.nags .nag{border-radius:0!important}.ui.nags .nag:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.bottom.nags .nag:last-child{border-radius:.28571429rem .28571429rem 0 0} -------------------------------------------------------------------------------- /public/css/components/rail.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Rail 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.rail{position:absolute;top:0;width:300px;height:100%}.ui.left.rail{left:auto;right:100%;padding:0 2rem 0 0;margin:0 2rem 0 0}.ui.right.rail{left:100%;right:auto;padding:0 0 0 2rem;margin:0 0 0 2rem}.ui.left.internal.rail{left:0;right:auto;padding:0 0 0 2rem;margin:0 0 0 2rem}.ui.right.internal.rail{left:auto;right:0;padding:0 2rem 0 0;margin:0 2rem 0 0}.ui.dividing.rail{width:302.5px}.ui.left.dividing.rail{padding:0 2.5rem 0 0;margin:0 2.5rem 0 0;border-right:1px solid rgba(34,36,38,.15)}.ui.right.dividing.rail{border-left:1px solid rgba(34,36,38,.15);padding:0 0 0 2.5rem;margin:0 0 0 2.5rem}.ui.close.rail{width:calc(300px + 1em)}.ui.close.left.rail{padding:0 1em 0 0;margin:0 1em 0 0}.ui.close.right.rail{padding:0 0 0 1em;margin:0 0 0 1em}.ui.very.close.rail{width:calc(300px + .5em)}.ui.very.close.left.rail{padding:0 .5em 0 0;margin:0 .5em 0 0}.ui.very.close.right.rail{padding:0 0 0 .5em;margin:0 0 0 .5em}.ui.attached.left.rail,.ui.attached.right.rail{padding:0;margin:0}.ui.mini.rail{font-size:.78571429rem}.ui.tiny.rail{font-size:.85714286rem}.ui.small.rail{font-size:.92857143rem}.ui.rail{font-size:1rem}.ui.large.rail{font-size:1.14285714rem}.ui.big.rail{font-size:1.28571429rem}.ui.huge.rail{font-size:1.42857143rem}.ui.massive.rail{font-size:1.71428571rem} -------------------------------------------------------------------------------- /views/signup.html: -------------------------------------------------------------------------------- 1 | {% extends './components/header.html'%} {% block main %} 2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |
37 |
38 | {% endblock %} -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | /* -------------全局样式 ------------*/ 2 | 3 | body{ 4 | width:1100px; 5 | height:100%; 6 | margin: 0 auto; 7 | padding-top: 40px; 8 | } 9 | 10 | a:hover{ 11 | border-bottom: 3px solid #4fc08d; 12 | } 13 | 14 | .button{ 15 | background-color: #4fc08d !important; 16 | color: #fff !important; 17 | } 18 | 19 | .avatar { 20 | border-radius: 3px; 21 | width:48px; 22 | height:48px; 23 | float:right; 24 | } 25 | 26 | /* ------------- nav ----------- */ 27 | 28 | .nav{ 29 | margin-bottom: 20px; 30 | color: #999; 31 | text-align:center; 32 | } 33 | 34 | .nav h1{ 35 | color: #4fc08d; 36 | display: inline-block; 37 | margin: 10px 0; 38 | } 39 | 40 | /* -----------nav-setting---------------- */ 41 | 42 | .nav-setting{ 43 | position:fixed; 44 | right:30px; 45 | } 46 | 47 | .nav-setting .ui.dropdown.button{ 48 | padding:10px 10px 0 10px; 49 | background-color: #ffffff !important; 50 | } 51 | 52 | .nav-setting .icon.bars{ 53 | color: #000000; 54 | font-size: 18px; 55 | } 56 | 57 | /* ------------- post-content ---------------- */ 58 | 59 | .post-content h3 a{ 60 | color: #4fc08d; !important; 61 | } 62 | 63 | .post-content .tag{ 64 | font-size: 13px; 65 | margin-right: 5px; 66 | color: #999999; 67 | } 68 | 69 | .post-content .tag.right{ 70 | float: right; 71 | margin-right: 0; 72 | } 73 | 74 | .post-content .tag-right a { 75 | color: #999999; 76 | } 77 | 78 | /* ---------------repeat ui------------------*/ 79 | 80 | .ui.selection.dropdown { 81 | min-height:2.3em; 82 | } -------------------------------------------------------------------------------- /views/posts.html: -------------------------------------------------------------------------------- 1 | {% extends './components/header.html' %}{% block main %} 2 | {% for item in posts%} 3 |
4 |
5 |
6 | 9 | 10 | 11 |
12 |
13 |
14 |

{{item.title}}

15 |
{{item.content | safe }}
16 |
17 | {{ item.created_at}} 18 | 19 | 浏览{{item.pv}} 20 | 留言{{item.commentsCount}} 21 | {% if user %} 22 | {% if item.author._id %} 23 | {% if (user._id.toString() === item.author._id.toString())%} 24 | 32 | {% endif %} 33 | {% endif %} 34 | {% endif %} 35 | 36 |
37 |
38 |
39 |
40 |
41 | {% endfor%} 42 | {% endblock %} -------------------------------------------------------------------------------- /package_back.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "f-blog", 3 | "version": "1.0.0", 4 | "description": "'floder's blog'", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "node --harmony ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha" 8 | }, 9 | "author": "floder", 10 | "license": "ISC", 11 | "dependencies": { 12 | "koa": "^2.0.1-alpha.8", 13 | "koa-bodyparser": "^3.2.0", //解析post请求的文本数据 14 | "koa-flash2": "0.0.2", //往session里添加flash消息 15 | "koa-generic-session": "^1.11.5", //session依赖的一个包 16 | "koa-logger-winston": "0.0.2", //日志包 17 | "koa-multer": "^1.0.1", //解析post请求的文件 18 | "koa-nunjucks-next": "^1.1.2", //适用于koa的nunjucks模板 19 | "koa-redis": "^2.1.3", //将session保存在redis中的一个中间件 20 | "koa-router": "^7.0.1", //路由文件 21 | "koa-session2": "^2.2.1", //koa2的session文件 22 | "koa-static": "^3.0.0", //静态文件映射的middleware 23 | "markdown": "^0.5.0", 24 | "marked": "^0.3.6", //将markdown格式的文件转换成html 25 | "mocha": "^3.2.0", //mocha测试 26 | "moment": "^2.17.1", //日期格式化工具 27 | "mongolass": "^2.4.2", //操作mongodb的一个工具 28 | "objectid-to-timestamp": "^1.3.0", //从ObjectId中抽取时间 29 | "redis": "^2.6.5", //操作redis的工具 30 | "semantic-ui": "^2.2.9", //前端工具semantic-ui,也可以使用cdn地址 31 | "supertest": "^3.0.0", //和mocha整合测试 32 | "winston": "^2.3.1" //winston模板 33 | }, 34 | "devDependencies": { 35 | "istanbul": "^0.4.5" //代码覆盖率工具 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /public/css/components/sticky.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Sticky 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Sticky 14 | *******************************/ 15 | 16 | .ui.sticky { 17 | position: static; 18 | -webkit-transition: none; 19 | transition: none; 20 | z-index: 800; 21 | } 22 | 23 | 24 | /******************************* 25 | States 26 | *******************************/ 27 | 28 | 29 | /* Bound */ 30 | .ui.sticky.bound { 31 | position: absolute; 32 | left: auto; 33 | right: auto; 34 | } 35 | 36 | /* Fixed */ 37 | .ui.sticky.fixed { 38 | position: fixed; 39 | left: auto; 40 | right: auto; 41 | } 42 | 43 | /* Bound/Fixed Position */ 44 | .ui.sticky.bound.top, 45 | .ui.sticky.fixed.top { 46 | top: 0px; 47 | bottom: auto; 48 | } 49 | .ui.sticky.bound.bottom, 50 | .ui.sticky.fixed.bottom { 51 | top: auto; 52 | bottom: 0px; 53 | } 54 | 55 | 56 | /******************************* 57 | Types 58 | *******************************/ 59 | 60 | .ui.native.sticky { 61 | position: -webkit-sticky; 62 | position: -moz-sticky; 63 | position: -ms-sticky; 64 | position: -o-sticky; 65 | position: sticky; 66 | } 67 | 68 | 69 | /******************************* 70 | Theme Overrides 71 | *******************************/ 72 | 73 | 74 | 75 | /******************************* 76 | Site Overrides 77 | *******************************/ 78 | 79 | -------------------------------------------------------------------------------- /routes/signin.js: -------------------------------------------------------------------------------- 1 | //处理所有登录开始的请求 2 | 3 | const koaRouter = require('koa-router'); 4 | let checkNotLogin = require('../middlewares/check').checkNotLogin; 5 | const crypto = require('crypto'); 6 | const userModel = require('../models/users'); 7 | 8 | const router = new koaRouter({ 9 | prefix: '/signin' 10 | }); 11 | 12 | //验证密码 13 | function auth(plainPassword,encryPassword){ 14 | let hash = crypto.createHash('sha256'); 15 | hash.update(plainPassword); 16 | return hash.digest('hex') === encryPassword ; 17 | } 18 | //GET /signin 登录页 19 | router.get('/',checkNotLogin,async(ctx,next) => { 20 | await ctx.render('signin',{title: '用户登录'}); 21 | }); 22 | 23 | //POST /signin 用户登录 24 | router.post('/',checkNotLogin,async(ctx,next) => { 25 | let name = ctx.request.body.name; 26 | let password = ctx.request.body.password; 27 | console.log("name: " + name + " ; " + "password: " + password); 28 | 29 | //根据用户名从mongodb找寻用户 30 | let user = await userModel.getUserByName(name); 31 | if(!user){ 32 | console.log('user not exist'); 33 | ctx.flash('error','用户不存在'); 34 | return ctx.redirect('back'); 35 | } 36 | console.log('user exists'); 37 | 38 | if(!auth(password,user.password)){ 39 | console.log('username or password is not right'); 40 | ctx.flash('error','用户名或者密码错误'); 41 | return ctx.redirect('back'); 42 | } 43 | 44 | ctx.flash('success','登录成功'); 45 | 46 | user.password = null; 47 | 48 | ctx.session.user = user; 49 | 50 | ctx.redirect('/posts'); 51 | }); 52 | 53 | module.exports = router; -------------------------------------------------------------------------------- /public/css/components/site.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Site 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin);body,html{height:100%}html{font-size:14px}body{margin:0;padding:0;overflow-x:hidden;min-width:320px;background:#fff;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:14px;line-height:1.4285em;color:rgba(0,0,0,.87);font-smoothing:antialiased}h1,h2,h3,h4,h5{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;line-height:1.28571429em;margin:calc(2rem - .14285714em) 0 1rem;font-weight:700;padding:0}h1{min-height:1rem;font-size:2rem}h2{font-size:1.71428571rem}h3{font-size:1.28571429rem}h4{font-size:1.07142857rem}h5{font-size:1rem}h1:first-child,h2:first-child,h3:first-child,h4:first-child,h5:first-child{margin-top:0}h1:last-child,h2:last-child,h3:last-child,h4:last-child,h5:last-child{margin-bottom:0}p{margin:0 0 1em;line-height:1.4285em}p:first-child{margin-top:0}p:last-child{margin-bottom:0}a{color:#4183c4;text-decoration:none}a:hover{color:#1e70bf;text-decoration:none}::-webkit-selection{background-color:#cce2ff;color:rgba(0,0,0,.87)}::-moz-selection{background-color:#cce2ff;color:rgba(0,0,0,.87)}::selection{background-color:#cce2ff;color:rgba(0,0,0,.87)}input::-webkit-selection,textarea::-webkit-selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.87)}input::-moz-selection,textarea::-moz-selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.87)}input::selection,textarea::selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.87)} -------------------------------------------------------------------------------- /models/comments.js: -------------------------------------------------------------------------------- 1 | let marked = require('marked'); 2 | let _Comment = require('../lib/mongo').Comment; 3 | 4 | //将comment的Content 从 markdown 转换成html 5 | _Comment.plugin('contentToHtml',{ 6 | afterFind: function(comments){ 7 | return comments.map(function(comment){ 8 | comment.content = marked(comment.content); 9 | return comment; 10 | }); 11 | }, 12 | afterFindOne: function(comment){ 13 | if(comment){ 14 | comment.content = marked(comment.content); 15 | } 16 | return comment; 17 | } 18 | }); 19 | 20 | module.exports = { 21 | 22 | //创建留言 23 | create : function create(comment){ 24 | return _Comment 25 | .create(comment) 26 | .exec(); 27 | }, 28 | 29 | //通过用户id 和留言id 删除一个留言 30 | delCommentById : function delCommentById(commentId,author){ 31 | return _Comment.remove({author:author,_id:commentId}).exec(); 32 | }, 33 | 34 | //通过文章id 删除该文章下所有留言 35 | delCommentsByPostId : function delCommentsByPostId(postId){ 36 | return _Comment.remove({postId: postId}).exec(); 37 | }, 38 | 39 | //通过文章id 获取该文章下所有留言,按留言创建时间排序 40 | getComments : function getComments(postId){ 41 | return _Comment.find({postId: postId}) 42 | .populate({path:'author',model:'User'}) 43 | .sort({_id: 1}) 44 | .addCreatedAt() 45 | .contentToHtml() 46 | .exec(); 47 | }, 48 | 49 | //通过文章id获取该文章下留言数 50 | getCommentsCount: function getCommentsCount(postId){ 51 | return _Comment.count({postId : postId}).exec(); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /views/components/comments.html: -------------------------------------------------------------------------------- 1 | {% extends '../post.html' %}{% block comment %} 2 |
3 |
4 |
5 |
6 |
7 |

留言

8 | 9 | {% for comment in comments %} 10 |
11 | 12 | 13 | 14 |
15 | {{ comment.author.name}} 16 | 19 |
{{ comment.content }}
20 | 21 | {% if user %} 22 | {% if comment.author._id %} 23 | {% if user._id.toString() ==== comment.author._id.toString() %} 24 |
25 | 删除 26 |
27 | {% endif %} 28 | {% endif %} 29 | {% endif %} 30 |
31 |
32 | {% endfor %} 33 | 34 | {% if user %} 35 |
36 |
37 | 38 |
39 | 40 |
41 | {% endif %} 42 | 43 |
44 |
45 |
46 |
47 | {% endblock %} -------------------------------------------------------------------------------- /lib/mongo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * mongo连接以及输出Schema模型 3 | */ 4 | 5 | const config = require('../config/default'); 6 | const Mongolass = require('mongolass'); 7 | const moment = require('moment'); 8 | const objectIdToTimestamp = require('objectid-to-timestamp'); 9 | const mongolass = new Mongolass(config.mongodb); 10 | 11 | //根据id生成创建时间 created_at 12 | mongolass.plugin('addCreatedAt',{ 13 | afterFind: function (results){ 14 | results.forEach(item => item.created_at = moment(objectIdToTimestamp(item._id)).format('YYYY-MM-DD HH:mm')); 15 | return results; 16 | }, 17 | afterFindOne: function(result){ 18 | if(result){ 19 | result.created_at = moment(objectIdToTimestamp(result._id)).format('YYYY-MM-DD HH:mm'); 20 | } 21 | return result; 22 | } 23 | }); 24 | 25 | let User = mongolass.model('user',{ 26 | name: {type: 'string'}, 27 | password: {type : 'string'}, 28 | avatar: {type:'string'}, 29 | gender:{type:'string',enum:['m','f','x']}, 30 | bio:{type:'string'} //个人简介 31 | }); 32 | 33 | User.index({name:1},{unique: true}).exec(); //name建立索引 34 | 35 | let Post = mongolass.model('Post',{ 36 | author: {type: Mongolass.Types.ObjectId}, 37 | title:{type: 'string'}, 38 | content: {type: 'string'}, 39 | pv: {type:'number'} //点击量 40 | }); 41 | 42 | Post.index({author:-1,_id: -1}).exec(); //按创建时间降序查看用户文章列表 43 | 44 | let Comments = mongolass.model('Comment',{ 45 | author: {type: Mongolass.Types.ObjectId}, 46 | content: { type: 'string'}, 47 | postId: {type: Mongolass.Types.ObjectId} 48 | }); 49 | 50 | Comments.index({postId: 1, _id: 1}).exec(); //通过文章id 获取该文章下所有留言 51 | Comments.index({author: 1,_id: 1}).exec(); //通过用户id和留言id 删除一个留言 52 | 53 | module.exports = { 54 | User : User, 55 | Post : Post, 56 | Comment: Comments 57 | }; 58 | 59 | 60 | -------------------------------------------------------------------------------- /public/css/components/embed.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Video 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.embed{position:relative;max-width:100%;height:0;overflow:hidden;background:#dcddde;padding-bottom:56.25%}.ui.embed embed,.ui.embed iframe,.ui.embed object{position:absolute;border:none;width:100%;height:100%;top:0;left:0;margin:0;padding:0}.ui.embed>.embed{display:none}.ui.embed>.placeholder{position:absolute;cursor:pointer;top:0;left:0;display:block;width:100%;height:100%;background-color:radial-gradient(transparent 45%,rgba(0,0,0,.3))}.ui.embed>.icon{cursor:pointer;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2}.ui.embed>.icon:after{position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;content:'';background:-webkit-radial-gradient(transparent 45%,rgba(0,0,0,.3));background:radial-gradient(transparent 45%,rgba(0,0,0,.3));opacity:.5;-webkit-transition:opacity .5s ease;transition:opacity .5s ease}.ui.embed>.icon:before{position:absolute;top:50%;left:50%;z-index:4;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);color:#fff;font-size:6rem;text-shadow:0 2px 10px rgba(34,36,38,.2);-webkit-transition:opacity .5s ease,color .5s ease;transition:opacity .5s ease,color .5s ease;z-index:10}.ui.embed .icon:hover:after{background:-webkit-radial-gradient(transparent 45%,rgba(0,0,0,.3));background:radial-gradient(transparent 45%,rgba(0,0,0,.3));opacity:1}.ui.embed .icon:hover:before{color:#fff}.ui.active.embed>.icon,.ui.active.embed>.placeholder{display:none}.ui.active.embed>.embed{display:block}.ui.square.embed{padding-bottom:100%}.ui[class*="4:3"].embed{padding-bottom:75%}.ui[class*="16:9"].embed{padding-bottom:56.25%}.ui[class*="21:9"].embed{padding-bottom:42.85714286%} -------------------------------------------------------------------------------- /public/css/components/container.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Container 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.container{display:block;max-width:100%!important}@media only screen and (max-width:767px){.ui.container{width:auto!important;margin-left:1em!important;margin-right:1em!important}.ui.grid.container{width:auto!important}.ui.relaxed.grid.container{width:auto!important}.ui.very.relaxed.grid.container{width:auto!important}}@media only screen and (min-width:768px) and (max-width:991px){.ui.container{width:723px;margin-left:auto!important;margin-right:auto!important}.ui.grid.container{width:calc(723px + 2rem)!important}.ui.relaxed.grid.container{width:calc(723px + 3rem)!important}.ui.very.relaxed.grid.container{width:calc(723px + 5rem)!important}}@media only screen and (min-width:992px) and (max-width:1199px){.ui.container{width:933px;margin-left:auto!important;margin-right:auto!important}.ui.grid.container{width:calc(933px + 2rem)!important}.ui.relaxed.grid.container{width:calc(933px + 3rem)!important}.ui.very.relaxed.grid.container{width:calc(933px + 5rem)!important}}@media only screen and (min-width:1200px){.ui.container{width:1127px;margin-left:auto!important;margin-right:auto!important}.ui.grid.container{width:calc(1127px + 2rem)!important}.ui.relaxed.grid.container{width:calc(1127px + 3rem)!important}.ui.very.relaxed.grid.container{width:calc(1127px + 5rem)!important}}.ui.text.container{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;max-width:700px!important;line-height:1.5}.ui.text.container{font-size:1.14285714rem}.ui.fluid.container{width:100%}.ui[class*="left aligned"].container{text-align:left}.ui[class*="center aligned"].container{text-align:center}.ui[class*="right aligned"].container{text-align:right}.ui.justified.container{text-align:justify;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto} -------------------------------------------------------------------------------- /public/css/components/ad.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Ad 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Copyright 2013 Contributors 7 | * Released under the MIT license 8 | * http://opensource.org/licenses/MIT 9 | * 10 | */.ui.ad{display:block;overflow:hidden;margin:1em 0}.ui.ad:first-child{margin:0}.ui.ad:last-child{margin:0}.ui.ad iframe{margin:0;padding:0;border:none;overflow:hidden}.ui.leaderboard.ad{width:728px;height:90px}.ui[class*="medium rectangle"].ad{width:300px;height:250px}.ui[class*="large rectangle"].ad{width:336px;height:280px}.ui[class*="half page"].ad{width:300px;height:600px}.ui.square.ad{width:250px;height:250px}.ui[class*="small square"].ad{width:200px;height:200px}.ui[class*="small rectangle"].ad{width:180px;height:150px}.ui[class*="vertical rectangle"].ad{width:240px;height:400px}.ui.button.ad{width:120px;height:90px}.ui[class*="square button"].ad{width:125px;height:125px}.ui[class*="small button"].ad{width:120px;height:60px}.ui.skyscraper.ad{width:120px;height:600px}.ui[class*="wide skyscraper"].ad{width:160px}.ui.banner.ad{width:468px;height:60px}.ui[class*="vertical banner"].ad{width:120px;height:240px}.ui[class*="top banner"].ad{width:930px;height:180px}.ui[class*="half banner"].ad{width:234px;height:60px}.ui[class*="large leaderboard"].ad{width:970px;height:90px}.ui.billboard.ad{width:970px;height:250px}.ui.panorama.ad{width:980px;height:120px}.ui.netboard.ad{width:580px;height:400px}.ui[class*="large mobile banner"].ad{width:320px;height:100px}.ui[class*="mobile leaderboard"].ad{width:320px;height:50px}.ui.mobile.ad{display:none}@media only screen and (max-width:767px){.ui.mobile.ad{display:block}}.ui.centered.ad{margin-left:auto;margin-right:auto}.ui.test.ad{position:relative;background:#545454}.ui.test.ad:after{position:absolute;top:50%;left:50%;width:100%;text-align:center;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);content:'Ad';color:#fff;font-size:1em;font-weight:700}.ui.mobile.test.ad:after{font-size:.85714286em}.ui.test.ad[data-text]:after{content:attr(data-text)} -------------------------------------------------------------------------------- /public/css/components/tab.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Tab 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | UI Tabs 14 | *******************************/ 15 | 16 | .ui.tab { 17 | display: none; 18 | } 19 | 20 | 21 | /******************************* 22 | States 23 | *******************************/ 24 | 25 | 26 | /*-------------------- 27 | Active 28 | ---------------------*/ 29 | 30 | .ui.tab.active, 31 | .ui.tab.open { 32 | display: block; 33 | } 34 | 35 | /*-------------------- 36 | Loading 37 | ---------------------*/ 38 | 39 | .ui.tab.loading { 40 | position: relative; 41 | overflow: hidden; 42 | display: block; 43 | min-height: 250px; 44 | } 45 | .ui.tab.loading * { 46 | position: relative !important; 47 | left: -10000px !important; 48 | } 49 | .ui.tab.loading:before, 50 | .ui.tab.loading.segment:before { 51 | position: absolute; 52 | content: ''; 53 | top: 100px; 54 | left: 50%; 55 | margin: -1.25em 0em 0em -1.25em; 56 | width: 2.5em; 57 | height: 2.5em; 58 | border-radius: 500rem; 59 | border: 0.2em solid rgba(0, 0, 0, 0.1); 60 | } 61 | .ui.tab.loading:after, 62 | .ui.tab.loading.segment:after { 63 | position: absolute; 64 | content: ''; 65 | top: 100px; 66 | left: 50%; 67 | margin: -1.25em 0em 0em -1.25em; 68 | width: 2.5em; 69 | height: 2.5em; 70 | -webkit-animation: button-spin 0.6s linear; 71 | animation: button-spin 0.6s linear; 72 | -webkit-animation-iteration-count: infinite; 73 | animation-iteration-count: infinite; 74 | border-radius: 500rem; 75 | border-color: #767676 transparent transparent; 76 | border-style: solid; 77 | border-width: 0.2em; 78 | box-shadow: 0px 0px 0px 1px transparent; 79 | } 80 | 81 | 82 | /******************************* 83 | Tab Overrides 84 | *******************************/ 85 | 86 | 87 | 88 | /******************************* 89 | User Overrides 90 | *******************************/ 91 | 92 | -------------------------------------------------------------------------------- /public/css/components/reset.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Reset 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */*,:after,:before{box-sizing:inherit}html{box-sizing:border-box}input[type=email],input[type=password],input[type=search],input[type=text]{-webkit-appearance:none;-moz-appearance:none}/*! normalize.css v3.0.1 | MIT License | git.io/normalize *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | //程序的入口 2 | /** 3 | * module dependency 4 | */ 5 | const path = require('path'); 6 | const Koa = require('koa'); 7 | const session = require('koa-generic-session'); 8 | const redisStore = require('koa-redis'); 9 | const flash = require('koa-flash2'); 10 | const config = require('./config/default'); 11 | const router = require('./routes'); 12 | const server = require('koa-static'); 13 | const redis = require('redis'); 14 | const template = require('./template'); 15 | const parse = require('koa-bodyparser'); 16 | const logger = require('./logger'); 17 | const koaLogger = require('koa-logger-winston'); 18 | const isProduction = process.env.NODE_ENV === 'production'; 19 | const app = new Koa(); 20 | 21 | //设置静态文件目录 22 | app.use(server(__dirname + '/public')); 23 | //session中间件 24 | let client = redis.createClient(config.redis_port,config.redis_host); 25 | app.keys = ['keys','f_blog_keys']; 26 | 27 | app.use(session({ 28 | store: redisStore({ 29 | // db:config.redis_db, 30 | client : client 31 | }) 32 | })); 33 | 34 | //flash中间件,用来显示通知 35 | app.use(flash()); 36 | 37 | app.use(parse()); 38 | 39 | console.log("isProduction: " + isProduction); 40 | 41 | //整合nunjucks 42 | app.use(template('views',{ 43 | noCache : !isProduction, 44 | watch : !isProduction 45 | })); 46 | 47 | //添加模板所需的三个变量 48 | app.use(async (ctx,next) => { 49 | 50 | Object.assign(ctx.state,{ 51 | user : ctx.session.user, 52 | success : ctx.flash('success').toString(), 53 | error : ctx.flash('error').toString() 54 | }); 55 | await next(); 56 | }); 57 | 58 | //正常请求的日志 59 | app.use(koaLogger(logger.success)); 60 | 61 | //路由 62 | app.use(router.routes()); 63 | 64 | //错误日志 65 | app.use(koaLogger(logger.error)); 66 | 67 | //处理404error 68 | app.use(async (ctx,next) => { 69 | if(ctx.status === 404){ 70 | await ctx.render('404',{title: '404 Page'}); 71 | } 72 | await next(); 73 | }); 74 | 75 | //处理500error页面 76 | app.on('error',async (err,ctx) => { 77 | await ctx.render('error',{title:'系统错误',error:err}); 78 | }); 79 | 80 | //如果index.js 被require了,则导出app,通常用于测试 81 | if(module.parent){ 82 | module.exports = app; 83 | }else{ 84 | app.listen(config.port,function(){ 85 | console.log(`f_blog listening on port ${config.port}`); 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /public/css/components/shape.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Shape 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.shape{position:relative;vertical-align:top;display:inline-block;-webkit-perspective:2000px;perspective:2000px;-webkit-transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out}.ui.shape .sides{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.ui.shape .side{opacity:1;width:100%;margin:0!important;-webkit-backface-visibility:hidden;backface-visibility:hidden}.ui.shape .side{display:none}.ui.shape .side *{-webkit-backface-visibility:visible!important;backface-visibility:visible!important}.ui.cube.shape .side{min-width:15em;height:15em;padding:2em;background-color:#e6e6e6;color:rgba(0,0,0,.87);box-shadow:0 0 2px rgba(0,0,0,.3)}.ui.cube.shape .side>.content{width:100%;height:100%;display:table;text-align:center;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.cube.shape .side>.content>div{display:table-cell;vertical-align:middle;font-size:2em}.ui.text.shape.animating .sides{position:static}.ui.text.shape .side{white-space:nowrap}.ui.text.shape .side>*{white-space:normal}.ui.loading.shape{position:absolute;top:-9999px;left:-9999px}.ui.shape .animating.side{position:absolute;top:0;left:0;display:block;z-index:100}.ui.shape .hidden.side{opacity:.6}.ui.shape.animating .sides{position:absolute}.ui.shape.animating .sides{-webkit-transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out}.ui.shape.animating .side{-webkit-transition:opacity .6s ease-in-out;transition:opacity .6s ease-in-out}.ui.shape .active.side{display:block} -------------------------------------------------------------------------------- /public/css/components/dimmer.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Dimmer 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.dimmable:not(.body){position:relative}.ui.dimmer{display:none;position:absolute;top:0!important;left:0!important;width:100%;height:100%;text-align:center;vertical-align:middle;background-color:rgba(0,0,0,.85);opacity:0;line-height:1;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-transition:background-color .5s linear;transition:background-color .5s linear;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;will-change:opacity;z-index:1000}.ui.dimmer>.content{width:100%;height:100%;display:table;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.dimmer>.content>*{display:table-cell;vertical-align:middle;color:#fff}.ui.segment>.ui.dimmer{border-radius:inherit!important}.animating.dimmable:not(body),.dimmed.dimmable:not(body){overflow:hidden}.dimmed.dimmable>.ui.animating.dimmer,.dimmed.dimmable>.ui.visible.dimmer,.ui.active.dimmer{display:block;opacity:1}.ui.disabled.dimmer{width:0!important;height:0!important}.ui.page.dimmer{position:fixed;-webkit-transform-style:'';transform-style:'';-webkit-perspective:2000px;perspective:2000px;-webkit-transform-origin:center center;transform-origin:center center}body.animating.in.dimmable,body.dimmed.dimmable{overflow:hidden}body.dimmable>.dimmer{position:fixed}.blurring.dimmable>:not(.dimmer){-webkit-filter:blur(0) grayscale(0);filter:blur(0) grayscale(0);-webkit-transition:.8s -webkit-filter ease;transition:.8s -webkit-filter ease;transition:.8s filter ease;transition:.8s filter ease,.8s -webkit-filter ease}.blurring.dimmed.dimmable>:not(.dimmer){-webkit-filter:blur(5px) grayscale(.7);filter:blur(5px) grayscale(.7)}.blurring.dimmable>.dimmer{background-color:rgba(0,0,0,.6)}.blurring.dimmable>.inverted.dimmer{background-color:rgba(255,255,255,.6)}.ui.dimmer>.top.aligned.content>*{vertical-align:top}.ui.dimmer>.bottom.aligned.content>*{vertical-align:bottom}.ui.inverted.dimmer{background-color:rgba(255,255,255,.85)}.ui.inverted.dimmer>.content>*{color:#fff}.ui.simple.dimmer{display:block;overflow:hidden;opacity:1;width:0;height:0%;z-index:-100;background-color:rgba(0,0,0,0)}.dimmed.dimmable>.ui.simple.dimmer{overflow:visible;opacity:1;width:100%;height:100%;background-color:rgba(0,0,0,.85);z-index:1}.ui.simple.inverted.dimmer{background-color:rgba(255,255,255,0)}.dimmed.dimmable>.ui.simple.inverted.dimmer{background-color:rgba(255,255,255,.85)} -------------------------------------------------------------------------------- /public/css/components/breadcrumb.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Breadcrumb 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Breadcrumb 14 | *******************************/ 15 | 16 | .ui.breadcrumb { 17 | line-height: 1; 18 | display: inline-block; 19 | margin: 0em 0em; 20 | vertical-align: middle; 21 | } 22 | .ui.breadcrumb:first-child { 23 | margin-top: 0em; 24 | } 25 | .ui.breadcrumb:last-child { 26 | margin-bottom: 0em; 27 | } 28 | 29 | 30 | /******************************* 31 | Content 32 | *******************************/ 33 | 34 | 35 | /* Divider */ 36 | .ui.breadcrumb .divider { 37 | display: inline-block; 38 | opacity: 0.7; 39 | margin: 0em 0.21428571rem 0em; 40 | font-size: 0.92857143em; 41 | color: rgba(0, 0, 0, 0.4); 42 | vertical-align: baseline; 43 | } 44 | 45 | /* Link */ 46 | .ui.breadcrumb a { 47 | color: #4183C4; 48 | } 49 | .ui.breadcrumb a:hover { 50 | color: #1e70bf; 51 | } 52 | 53 | /* Icon Divider */ 54 | .ui.breadcrumb .icon.divider { 55 | font-size: 0.85714286em; 56 | vertical-align: baseline; 57 | } 58 | 59 | /* Section */ 60 | .ui.breadcrumb a.section { 61 | cursor: pointer; 62 | } 63 | .ui.breadcrumb .section { 64 | display: inline-block; 65 | margin: 0em; 66 | padding: 0em; 67 | } 68 | 69 | /* Loose Coupling */ 70 | .ui.breadcrumb.segment { 71 | display: inline-block; 72 | padding: 0.78571429em 1em; 73 | } 74 | 75 | 76 | /******************************* 77 | States 78 | *******************************/ 79 | 80 | .ui.breadcrumb .active.section { 81 | font-weight: bold; 82 | } 83 | 84 | 85 | /******************************* 86 | Variations 87 | *******************************/ 88 | 89 | .ui.mini.breadcrumb { 90 | font-size: 0.78571429rem; 91 | } 92 | .ui.tiny.breadcrumb { 93 | font-size: 0.85714286rem; 94 | } 95 | .ui.small.breadcrumb { 96 | font-size: 0.92857143rem; 97 | } 98 | .ui.breadcrumb { 99 | font-size: 1rem; 100 | } 101 | .ui.large.breadcrumb { 102 | font-size: 1.14285714rem; 103 | } 104 | .ui.big.breadcrumb { 105 | font-size: 1.28571429rem; 106 | } 107 | .ui.huge.breadcrumb { 108 | font-size: 1.42857143rem; 109 | } 110 | .ui.massive.breadcrumb { 111 | font-size: 1.71428571rem; 112 | } 113 | 114 | 115 | /******************************* 116 | Theme Overrides 117 | *******************************/ 118 | 119 | 120 | 121 | /******************************* 122 | Site Overrides 123 | *******************************/ 124 | 125 | -------------------------------------------------------------------------------- /views/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{title}} - floder 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% block body %}{% endblock %} 13 | 82 | 83 | -------------------------------------------------------------------------------- /public/css/components/comment.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Comment 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.comments{margin:1.5em 0;max-width:650px}.ui.comments:first-child{margin-top:0}.ui.comments:last-child{margin-bottom:0}.ui.comments .comment{position:relative;background:0 0;margin:.5em 0 0;padding:.5em 0 0;border:none;border-top:none;line-height:1.2}.ui.comments .comment:first-child{margin-top:0;padding-top:0}.ui.comments .comment .comments{margin:0 0 .5em .5em;padding:1em 0 1em 1em}.ui.comments .comment .comments:before{position:absolute;top:0;left:0}.ui.comments .comment .comments .comment{border:none;border-top:none;background:0 0}.ui.comments .comment .avatar{display:block;width:2.5em;height:auto;float:left;margin:.2em 0 0}.ui.comments .comment .avatar img,.ui.comments .comment img.avatar{display:block;margin:0 auto;width:100%;height:100%;border-radius:.25rem}.ui.comments .comment>.content{display:block}.ui.comments .comment>.avatar~.content{margin-left:3.5em}.ui.comments .comment .author{font-size:1em;color:rgba(0,0,0,.87);font-weight:700}.ui.comments .comment a.author{cursor:pointer}.ui.comments .comment a.author:hover{color:#1e70bf}.ui.comments .comment .metadata{display:inline-block;margin-left:.5em;color:rgba(0,0,0,.4);font-size:.875em}.ui.comments .comment .metadata>*{display:inline-block;margin:0 .5em 0 0}.ui.comments .comment .metadata>:last-child{margin-right:0}.ui.comments .comment .text{margin:.25em 0 .5em;font-size:1em;word-wrap:break-word;color:rgba(0,0,0,.87);line-height:1.3}.ui.comments .comment .actions{font-size:.875em}.ui.comments .comment .actions a{cursor:pointer;display:inline-block;margin:0 .75em 0 0;color:rgba(0,0,0,.4)}.ui.comments .comment .actions a:last-child{margin-right:0}.ui.comments .comment .actions a.active,.ui.comments .comment .actions a:hover{color:rgba(0,0,0,.8)}.ui.comments>.reply.form{margin-top:1em}.ui.comments .comment .reply.form{width:100%;margin-top:1em}.ui.comments .reply.form textarea{font-size:1em;height:12em}.ui.collapsed.comments,.ui.comments .collapsed.comment,.ui.comments .collapsed.comments{display:none}.ui.threaded.comments .comment .comments{margin:-1.5em 0 -1em 1.25em;padding:3em 0 2em 2.25em;box-shadow:-1px 0 0 rgba(34,36,38,.15)}.ui.minimal.comments .comment .actions{opacity:0;position:absolute;top:0;right:0;left:auto;-webkit-transition:opacity .2s ease;transition:opacity .2s ease;-webkit-transition-delay:.1s;transition-delay:.1s}.ui.minimal.comments .comment>.content:hover>.actions{opacity:1}.ui.mini.comments{font-size:.78571429rem}.ui.tiny.comments{font-size:.85714286rem}.ui.small.comments{font-size:.92857143rem}.ui.comments{font-size:1rem}.ui.large.comments{font-size:1.14285714rem}.ui.big.comments{font-size:1.28571429rem}.ui.huge.comments{font-size:1.42857143rem}.ui.massive.comments{font-size:1.71428571rem} -------------------------------------------------------------------------------- /test/singup.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const assert = require('assert'); 3 | const request = require('supertest'); 4 | const app = require('../index'); //没导出? 5 | const User = require('../lib/mongo').User; 6 | 7 | let testname1 = 'testName1'; 8 | let testname2 = 'floder'; 9 | 10 | describe('signup',function(){ 11 | describe('POST /signup',function(){ 12 | let agent = request.agent(app); //persist cookie when redirect 13 | beforeEach(function (done){ 14 | User.create({ 15 | name:testname1, 16 | password:'123456', 17 | avatar:'', 18 | gender: 'x', 19 | bio: '' 20 | }).exec() 21 | .then(function(){ 22 | done(); 23 | }) 24 | .catch(done); 25 | }); 26 | afterEach(function(done){ 27 | //删除测试用户 28 | User.remove({name: {$in: [testname1,testname2]}}) 29 | .exec() 30 | .then(function(){ 31 | done(); 32 | }) 33 | .catch(done); 34 | }); 35 | 36 | //用户名错误的情况 37 | it('wrong name',function(done){ 38 | agent 39 | .post('/signup') 40 | .type('form') 41 | .attach('avatar',path.join(__dirname,'avatar.jpg')) 42 | .field({name:''}) 43 | .redirects() 44 | .end(function(err,res){ 45 | if(err) return done(err); 46 | assert(res.text.match(/名字长度为1-10/)); 47 | done(); 48 | }); 49 | }); 50 | 51 | // 用户名被占用的情况 52 | it('duplicate name', function(done) { 53 | agent 54 | .post('/signup') 55 | .type('form') 56 | .attach('avatar', path.join(__dirname, 'avatar.png')) 57 | .field({ name: testName1, gender: 'm', bio: 'noder', password: '123456', repassword: '123456' }) 58 | .redirects() 59 | .end(function(err, res) { 60 | if (err) return done(err); 61 | assert(res.text.match(/用户名已被占用/)); 62 | done(); 63 | }); 64 | }); 65 | 66 | // 注册成功的情况 67 | it('success', function(done) { 68 | agent 69 | .post('/signup') 70 | .type('form') 71 | .attach('avatar', path.join(__dirname, 'avatar.png')) 72 | .field({ name: testName2, gender: 'm', bio: 'noder', password: '123456', repassword: '123456' }) 73 | .redirects() 74 | .end(function(err, res) { 75 | if (err) return done(err); 76 | assert(res.text.match(/注册成功/)); 77 | done(); 78 | }); 79 | }); 80 | }); 81 | }); 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /routes/signup.js: -------------------------------------------------------------------------------- 1 | //处理所有以 '/signup' 开头的请求 2 | 3 | const koaRouter = require('koa-router'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const crypto = require('crypto'); //加密模块 7 | const checkNotLogin = require('../middlewares/check').checkNotLogin; 8 | const userModel = require('../models/users'); 9 | // const formidable = require('koa-formidable'); 10 | const multer = require('koa-multer'); 11 | // const util = require('util'); 12 | let router = new koaRouter({ 13 | prefix: '/signup' 14 | }); 15 | 16 | const upload = multer({ 17 | dest: path.join(__dirname,"../public/img") 18 | }); 19 | 20 | function encrypt(plainPassword,method){ 21 | const hash = crypto.createHash(method); 22 | hash.update(plainPassword); 23 | return hash.digest('hex'); 24 | }; 25 | 26 | //GET /signup 注册页 27 | router.get('/',checkNotLogin,async(ctx,next)=> { 28 | await ctx.render('signup',{title:'signup'}); 29 | }); 30 | //POST /signup 用户注册 31 | router.post('/',checkNotLogin,upload.single('avatar'),async(ctx,next) =>{ 32 | let req = ctx.req; 33 | let name = req.body.name; 34 | let gender = req.body.gender; 35 | let bio = req.body.bio; 36 | let avatar; 37 | let password = req.body.password; 38 | let repassword = req.body.repassword; 39 | 40 | try{ 41 | if(!(name.trim().length) >= 1 && name.trim().length <= 10){ 42 | ctx.flash('error','名字长度为1-10'); 43 | } 44 | if(['m','f','x'].indexOf(gender) === -1){ 45 | ctx.flash('error','性别不对'); 46 | } 47 | if(!(bio.trim().length >= 1 && bio.trim().length <= 30)){ 48 | ctx.flash('error','简介不能超过30个字符'); 49 | } 50 | if(!ctx.req.file.originalname){ 51 | ctx.flash('error','需要上传头像'); 52 | } 53 | if(password.length < 6){ 54 | ctx.flash('error','密码长度必须大于6'); 55 | } 56 | if(password !== repassword){ 57 | ctx.flash('error','两次密码不同'); 58 | } 59 | avatar = req.file.path.split('/').pop(); //返回文件的名字 60 | }catch(e){ 61 | //注册失败,异步删除以上传的图片 62 | fs.unlink(ctx.req.file.path); 63 | //往session里面加入一条错误消息 64 | ctx.flash('error',e.message); 65 | return ctx.redirect('/signup'); 66 | } 67 | //密码明文加密 68 | password = encrypt(password,'sha256'); 69 | 70 | //待写入数据库信息 71 | let user = { 72 | name: name, 73 | password: password, 74 | gender: gender, 75 | bio: bio, 76 | avatar: avatar //保存的是文件的名字 77 | }; 78 | 79 | console.log(user); 80 | 81 | //将用户信息写入数据库 82 | try{ 83 | let create = await userModel.create(user); 84 | user = create.ops[0]; 85 | user.password = null; 86 | ctx.session.user = user; 87 | //写入flash消息 88 | ctx.flash('success','注册成功'); 89 | }catch(e){ 90 | //删除上传的文件 91 | fs.unlink(ctx.req.file.path); 92 | if(e.errmsg.match('E11000 duplicate key')){ 93 | ctx.flash('error','用户名已被占用'); 94 | return ctx.redirect('/signup'); 95 | } 96 | } 97 | ctx.redirect('/posts'); 98 | }); 99 | 100 | module.exports = router; -------------------------------------------------------------------------------- /views/post.html: -------------------------------------------------------------------------------- 1 | {% extends './components/header.html' %}{% block main %} 2 | {% if item %} 3 |
4 |
5 |
6 | 9 | 10 | 11 |
12 |
13 |
14 |

{{item.title}}

15 |
{{item.content | safe }}
16 |
17 | {{ item.created_at}} 18 | 19 | 浏览{{item.pv}} 20 | 留言{{item.commentsCount}} 21 | {% if user %} 22 | {% if item.author._id %} 23 | {% if (user._id.toString() === item.author._id.toString())%} 24 | 32 | {% endif %} 33 | {% endif %} 34 | {% endif %} 35 | 36 |
37 |
38 |
39 |
40 |
41 | {% endif %} 42 | 43 |
44 |
45 |
46 |
47 |
48 |

留言

49 | 50 | {% for comment in comments %} 51 |
52 | 53 | 54 | 55 |
56 | {{ comment.author.name}} 57 | 60 |
{{ comment.content | safe }}
61 | 62 | {% if user %} 63 | {% if comment.author._id %} 64 | {% if user._id.toString() === comment.author._id.toString() %} 65 |
66 | 删除 67 |
68 | {% endif %} 69 | {% endif %} 70 | {% endif %} 71 |
72 |
73 | {% endfor %} 74 | 75 | {% if user %} 76 |
77 |
78 | 79 |
80 | 81 |
82 | {% endif %} 83 | 84 |
85 |
86 |
87 |
88 | 89 | 90 | 94 | {% endblock %} -------------------------------------------------------------------------------- /public/css/components/nag.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Nag 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Nag 14 | *******************************/ 15 | 16 | .ui.nag { 17 | display: none; 18 | opacity: 0.95; 19 | position: relative; 20 | top: 0em; 21 | left: 0px; 22 | z-index: 999; 23 | min-height: 0em; 24 | width: 100%; 25 | margin: 0em; 26 | padding: 0.75em 1em; 27 | background: #555555; 28 | box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); 29 | font-size: 1rem; 30 | text-align: center; 31 | color: rgba(0, 0, 0, 0.87); 32 | border-radius: 0em 0em 0.28571429rem 0.28571429rem; 33 | -webkit-transition: 0.2s background ease; 34 | transition: 0.2s background ease; 35 | } 36 | a.ui.nag { 37 | cursor: pointer; 38 | } 39 | .ui.nag > .title { 40 | display: inline-block; 41 | margin: 0em 0.5em; 42 | color: #FFFFFF; 43 | } 44 | .ui.nag > .close.icon { 45 | cursor: pointer; 46 | opacity: 0.4; 47 | position: absolute; 48 | top: 50%; 49 | right: 1em; 50 | font-size: 1em; 51 | margin: -0.5em 0em 0em; 52 | color: #FFFFFF; 53 | -webkit-transition: opacity 0.2s ease; 54 | transition: opacity 0.2s ease; 55 | } 56 | 57 | 58 | /******************************* 59 | States 60 | *******************************/ 61 | 62 | 63 | /* Hover */ 64 | .ui.nag:hover { 65 | background: #555555; 66 | opacity: 1; 67 | } 68 | .ui.nag .close:hover { 69 | opacity: 1; 70 | } 71 | 72 | 73 | /******************************* 74 | Variations 75 | *******************************/ 76 | 77 | 78 | /*-------------- 79 | Static 80 | ---------------*/ 81 | 82 | .ui.overlay.nag { 83 | position: absolute; 84 | display: block; 85 | } 86 | 87 | /*-------------- 88 | Fixed 89 | ---------------*/ 90 | 91 | .ui.fixed.nag { 92 | position: fixed; 93 | } 94 | 95 | /*-------------- 96 | Bottom 97 | ---------------*/ 98 | 99 | .ui.bottom.nags, 100 | .ui.bottom.nag { 101 | border-radius: 0.28571429rem 0.28571429rem 0em 0em; 102 | top: auto; 103 | bottom: 0em; 104 | } 105 | 106 | /*-------------- 107 | White 108 | ---------------*/ 109 | 110 | .ui.inverted.nags .nag, 111 | .ui.inverted.nag { 112 | background-color: #F3F4F5; 113 | color: rgba(0, 0, 0, 0.85); 114 | } 115 | .ui.inverted.nags .nag .close, 116 | .ui.inverted.nags .nag .title, 117 | .ui.inverted.nag .close, 118 | .ui.inverted.nag .title { 119 | color: rgba(0, 0, 0, 0.4); 120 | } 121 | 122 | 123 | /******************************* 124 | Groups 125 | *******************************/ 126 | 127 | .ui.nags .nag { 128 | border-radius: 0em !important; 129 | } 130 | .ui.nags .nag:last-child { 131 | border-radius: 0em 0em 0.28571429rem 0.28571429rem; 132 | } 133 | .ui.bottom.nags .nag:last-child { 134 | border-radius: 0.28571429rem 0.28571429rem 0em 0em; 135 | } 136 | 137 | 138 | /******************************* 139 | Theme Overrides 140 | *******************************/ 141 | 142 | 143 | 144 | /******************************* 145 | User Overrides 146 | *******************************/ 147 | 148 | -------------------------------------------------------------------------------- /public/css/components/rail.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Rail 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Rails 14 | *******************************/ 15 | 16 | .ui.rail { 17 | position: absolute; 18 | top: 0%; 19 | width: 300px; 20 | height: 100%; 21 | } 22 | .ui.left.rail { 23 | left: auto; 24 | right: 100%; 25 | padding: 0em 2rem 0em 0em; 26 | margin: 0em 2rem 0em 0em; 27 | } 28 | .ui.right.rail { 29 | left: 100%; 30 | right: auto; 31 | padding: 0em 0em 0em 2rem; 32 | margin: 0em 0em 0em 2rem; 33 | } 34 | 35 | 36 | /******************************* 37 | Variations 38 | *******************************/ 39 | 40 | 41 | /*-------------- 42 | Internal 43 | ---------------*/ 44 | 45 | .ui.left.internal.rail { 46 | left: 0%; 47 | right: auto; 48 | padding: 0em 0em 0em 2rem; 49 | margin: 0em 0em 0em 2rem; 50 | } 51 | .ui.right.internal.rail { 52 | left: auto; 53 | right: 0%; 54 | padding: 0em 2rem 0em 0em; 55 | margin: 0em 2rem 0em 0em; 56 | } 57 | 58 | /*-------------- 59 | Dividing 60 | ---------------*/ 61 | 62 | .ui.dividing.rail { 63 | width: 302.5px; 64 | } 65 | .ui.left.dividing.rail { 66 | padding: 0em 2.5rem 0em 0em; 67 | margin: 0em 2.5rem 0em 0em; 68 | border-right: 1px solid rgba(34, 36, 38, 0.15); 69 | } 70 | .ui.right.dividing.rail { 71 | border-left: 1px solid rgba(34, 36, 38, 0.15); 72 | padding: 0em 0em 0em 2.5rem; 73 | margin: 0em 0em 0em 2.5rem; 74 | } 75 | 76 | /*-------------- 77 | Distance 78 | ---------------*/ 79 | 80 | .ui.close.rail { 81 | width: calc( 300px + 1em ); 82 | } 83 | .ui.close.left.rail { 84 | padding: 0em 1em 0em 0em; 85 | margin: 0em 1em 0em 0em; 86 | } 87 | .ui.close.right.rail { 88 | padding: 0em 0em 0em 1em; 89 | margin: 0em 0em 0em 1em; 90 | } 91 | .ui.very.close.rail { 92 | width: calc( 300px + 0.5em ); 93 | } 94 | .ui.very.close.left.rail { 95 | padding: 0em 0.5em 0em 0em; 96 | margin: 0em 0.5em 0em 0em; 97 | } 98 | .ui.very.close.right.rail { 99 | padding: 0em 0em 0em 0.5em; 100 | margin: 0em 0em 0em 0.5em; 101 | } 102 | 103 | /*-------------- 104 | Attached 105 | ---------------*/ 106 | 107 | .ui.attached.left.rail, 108 | .ui.attached.right.rail { 109 | padding: 0em; 110 | margin: 0em; 111 | } 112 | 113 | /*-------------- 114 | Sizing 115 | ---------------*/ 116 | 117 | .ui.mini.rail { 118 | font-size: 0.78571429rem; 119 | } 120 | .ui.tiny.rail { 121 | font-size: 0.85714286rem; 122 | } 123 | .ui.small.rail { 124 | font-size: 0.92857143rem; 125 | } 126 | .ui.rail { 127 | font-size: 1rem; 128 | } 129 | .ui.large.rail { 130 | font-size: 1.14285714rem; 131 | } 132 | .ui.big.rail { 133 | font-size: 1.28571429rem; 134 | } 135 | .ui.huge.rail { 136 | font-size: 1.42857143rem; 137 | } 138 | .ui.massive.rail { 139 | font-size: 1.71428571rem; 140 | } 141 | 142 | 143 | /******************************* 144 | Theme Overrides 145 | *******************************/ 146 | 147 | 148 | 149 | /******************************* 150 | Site Overrides 151 | *******************************/ 152 | 153 | -------------------------------------------------------------------------------- /public/css/components/site.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Site 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Page 14 | *******************************/ 15 | 16 | @import url('https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin'); 17 | html, 18 | body { 19 | height: 100%; 20 | } 21 | html { 22 | font-size: 14px; 23 | } 24 | body { 25 | margin: 0px; 26 | padding: 0px; 27 | overflow-x: hidden; 28 | min-width: 320px; 29 | background: #FFFFFF; 30 | font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; 31 | font-size: 14px; 32 | line-height: 1.4285em; 33 | color: rgba(0, 0, 0, 0.87); 34 | font-smoothing: antialiased; 35 | } 36 | 37 | 38 | /******************************* 39 | Headers 40 | *******************************/ 41 | 42 | h1, 43 | h2, 44 | h3, 45 | h4, 46 | h5 { 47 | font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; 48 | line-height: 1.28571429em; 49 | margin: calc(2rem - 0.14285714em ) 0em 1rem; 50 | font-weight: bold; 51 | padding: 0em; 52 | } 53 | h1 { 54 | min-height: 1rem; 55 | font-size: 2rem; 56 | } 57 | h2 { 58 | font-size: 1.71428571rem; 59 | } 60 | h3 { 61 | font-size: 1.28571429rem; 62 | } 63 | h4 { 64 | font-size: 1.07142857rem; 65 | } 66 | h5 { 67 | font-size: 1rem; 68 | } 69 | h1:first-child, 70 | h2:first-child, 71 | h3:first-child, 72 | h4:first-child, 73 | h5:first-child { 74 | margin-top: 0em; 75 | } 76 | h1:last-child, 77 | h2:last-child, 78 | h3:last-child, 79 | h4:last-child, 80 | h5:last-child { 81 | margin-bottom: 0em; 82 | } 83 | 84 | 85 | /******************************* 86 | Text 87 | *******************************/ 88 | 89 | p { 90 | margin: 0em 0em 1em; 91 | line-height: 1.4285em; 92 | } 93 | p:first-child { 94 | margin-top: 0em; 95 | } 96 | p:last-child { 97 | margin-bottom: 0em; 98 | } 99 | 100 | /*------------------- 101 | Links 102 | --------------------*/ 103 | 104 | a { 105 | color: #4183C4; 106 | text-decoration: none; 107 | } 108 | a:hover { 109 | color: #1e70bf; 110 | text-decoration: none; 111 | } 112 | 113 | 114 | /******************************* 115 | Highlighting 116 | *******************************/ 117 | 118 | 119 | /* Site */ 120 | ::-webkit-selection { 121 | background-color: #CCE2FF; 122 | color: rgba(0, 0, 0, 0.87); 123 | } 124 | ::-moz-selection { 125 | background-color: #CCE2FF; 126 | color: rgba(0, 0, 0, 0.87); 127 | } 128 | ::selection { 129 | background-color: #CCE2FF; 130 | color: rgba(0, 0, 0, 0.87); 131 | } 132 | 133 | /* Form */ 134 | textarea::-webkit-selection, 135 | input::-webkit-selection { 136 | background-color: rgba(100, 100, 100, 0.4); 137 | color: rgba(0, 0, 0, 0.87); 138 | } 139 | textarea::-moz-selection, 140 | input::-moz-selection { 141 | background-color: rgba(100, 100, 100, 0.4); 142 | color: rgba(0, 0, 0, 0.87); 143 | } 144 | textarea::selection, 145 | input::selection { 146 | background-color: rgba(100, 100, 100, 0.4); 147 | color: rgba(0, 0, 0, 0.87); 148 | } 149 | 150 | 151 | /******************************* 152 | Global Overrides 153 | *******************************/ 154 | 155 | 156 | 157 | /******************************* 158 | Site Overrides 159 | *******************************/ 160 | 161 | -------------------------------------------------------------------------------- /public/css/components/feed.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Feed 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.feed{margin:1em 0}.ui.feed:first-child{margin-top:0}.ui.feed:last-child{margin-bottom:0}.ui.feed>.event{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;width:100%;padding:.21428571rem 0;margin:0;background:0 0;border-top:none}.ui.feed>.event:first-child{border-top:0;padding-top:0}.ui.feed>.event:last-child{padding-bottom:0}.ui.feed>.event>.label{display:block;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:2.5em;height:auto;-ms-flex-item-align:stretch;-ms-grid-row-align:stretch;align-self:stretch;text-align:left}.ui.feed>.event>.label .icon{opacity:1;font-size:1.5em;width:100%;padding:.25em;background:0 0;border:none;border-radius:none;color:rgba(0,0,0,.6)}.ui.feed>.event>.label img{width:100%;height:auto;border-radius:500rem}.ui.feed>.event>.label+.content{margin:.5em 0 .35714286em 1.14285714em}.ui.feed>.event>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;-ms-flex-item-align:stretch;-ms-grid-row-align:stretch;align-self:stretch;text-align:left;word-wrap:break-word}.ui.feed>.event:last-child>.content{padding-bottom:0}.ui.feed>.event>.content a{cursor:pointer}.ui.feed>.event>.content .date{margin:-.5rem 0 0;padding:0;font-weight:400;font-size:1em;font-style:normal;color:rgba(0,0,0,.4)}.ui.feed>.event>.content .summary{margin:0;font-size:1em;font-weight:700;color:rgba(0,0,0,.87)}.ui.feed>.event>.content .summary img{display:inline-block;width:auto;height:10em;margin:-.25em .25em 0 0;border-radius:.25em;vertical-align:middle}.ui.feed>.event>.content .user{display:inline-block;font-weight:700;margin-right:0;vertical-align:baseline}.ui.feed>.event>.content .user img{margin:-.25em .25em 0 0;width:auto;height:10em;vertical-align:middle}.ui.feed>.event>.content .summary>.date{display:inline-block;float:none;font-weight:400;font-size:.85714286em;font-style:normal;margin:0 0 0 .5em;padding:0;color:rgba(0,0,0,.4)}.ui.feed>.event>.content .extra{margin:.5em 0 0;background:0 0;padding:0;color:rgba(0,0,0,.87)}.ui.feed>.event>.content .extra.images img{display:inline-block;margin:0 .25em 0 0;width:6em}.ui.feed>.event>.content .extra.text{padding:0;border-left:none;font-size:1em;max-width:500px;line-height:1.4285em}.ui.feed>.event>.content .meta{display:inline-block;font-size:.85714286em;margin:.5em 0 0;background:0 0;border:none;border-radius:0;box-shadow:none;padding:0;color:rgba(0,0,0,.6)}.ui.feed>.event>.content .meta>*{position:relative;margin-left:.75em}.ui.feed>.event>.content .meta>:after{content:'';color:rgba(0,0,0,.2);top:0;left:-1em;opacity:1;position:absolute;vertical-align:top}.ui.feed>.event>.content .meta .like{color:'';-webkit-transition:.2s color ease;transition:.2s color ease}.ui.feed>.event>.content .meta .like:hover .icon{color:#ff2733}.ui.feed>.event>.content .meta .active.like .icon{color:#ef404a}.ui.feed>.event>.content .meta>:first-child{margin-left:0}.ui.feed>.event>.content .meta>:first-child::after{display:none}.ui.feed>.event>.content .meta a,.ui.feed>.event>.content .meta>.icon{cursor:pointer;opacity:1;color:rgba(0,0,0,.5);-webkit-transition:color .1s ease;transition:color .1s ease}.ui.feed>.event>.content .meta a:hover,.ui.feed>.event>.content .meta a:hover .icon,.ui.feed>.event>.content .meta>.icon:hover{color:rgba(0,0,0,.95)}.ui.small.feed{font-size:.92857143rem}.ui.feed{font-size:1rem}.ui.large.feed{font-size:1.14285714rem} -------------------------------------------------------------------------------- /public/css/components/image.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Image 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.image{position:relative;display:inline-block;vertical-align:middle;max-width:100%;background-color:transparent}img.ui.image{display:block}.ui.image img,.ui.image svg{display:block;max-width:100%;height:auto}.ui.hidden.image,.ui.hidden.images{display:none}.ui.hidden.transition.image,.ui.hidden.transition.images{display:block;visibility:hidden}.ui.disabled.image,.ui.disabled.images{cursor:default;opacity:.45}.ui.inline.image,.ui.inline.image img,.ui.inline.image svg{display:inline-block}.ui.top.aligned.image,.ui.top.aligned.image img,.ui.top.aligned.image svg,.ui.top.aligned.images .image{display:inline-block;vertical-align:top}.ui.middle.aligned.image,.ui.middle.aligned.image img,.ui.middle.aligned.image svg,.ui.middle.aligned.images .image{display:inline-block;vertical-align:middle}.ui.bottom.aligned.image,.ui.bottom.aligned.image img,.ui.bottom.aligned.image svg,.ui.bottom.aligned.images .image{display:inline-block;vertical-align:bottom}.ui.rounded.image,.ui.rounded.image>*,.ui.rounded.images .image,.ui.rounded.images .image>*{border-radius:.3125em}.ui.bordered.image img,.ui.bordered.image svg,.ui.bordered.images .image,.ui.bordered.images img,.ui.bordered.images svg,img.ui.bordered.image{border:1px solid rgba(0,0,0,.1)}.ui.circular.image,.ui.circular.images{overflow:hidden}.ui.circular.image,.ui.circular.image>*,.ui.circular.images .image,.ui.circular.images .image>*{border-radius:500rem}.ui.fluid.image,.ui.fluid.image img,.ui.fluid.image svg,.ui.fluid.images,.ui.fluid.images img,.ui.fluid.images svg{display:block;width:100%;height:auto}.ui.avatar.image,.ui.avatar.image img,.ui.avatar.image svg,.ui.avatar.images .image,.ui.avatar.images img,.ui.avatar.images svg{margin-right:.25em;display:inline-block;width:2em;height:2em;border-radius:500rem}.ui.spaced.image{display:inline-block!important;margin-left:.5em;margin-right:.5em}.ui[class*="left spaced"].image{margin-left:.5em;margin-right:0}.ui[class*="right spaced"].image{margin-left:0;margin-right:.5em}.ui.floated.image,.ui.floated.images{float:left;margin-right:1em;margin-bottom:1em}.ui.right.floated.image,.ui.right.floated.images{float:right;margin-right:0;margin-bottom:1em;margin-left:1em}.ui.floated.image:last-child,.ui.floated.images:last-child{margin-bottom:0}.ui.centered.image,.ui.centered.images{margin-left:auto;margin-right:auto}.ui.mini.image,.ui.mini.images .image,.ui.mini.images img,.ui.mini.images svg{width:35px;height:auto;font-size:.78571429rem}.ui.tiny.image,.ui.tiny.images .image,.ui.tiny.images img,.ui.tiny.images svg{width:80px;height:auto;font-size:.85714286rem}.ui.small.image,.ui.small.images .image,.ui.small.images img,.ui.small.images svg{width:150px;height:auto;font-size:.92857143rem}.ui.medium.image,.ui.medium.images .image,.ui.medium.images img,.ui.medium.images svg{width:300px;height:auto;font-size:1rem}.ui.large.image,.ui.large.images .image,.ui.large.images img,.ui.large.images svg{width:450px;height:auto;font-size:1.14285714rem}.ui.big.image,.ui.big.images .image,.ui.big.images img,.ui.big.images svg{width:600px;height:auto;font-size:1.28571429rem}.ui.huge.image,.ui.huge.images .image,.ui.huge.images img,.ui.huge.images svg{width:800px;height:auto;font-size:1.42857143rem}.ui.massive.image,.ui.massive.images .image,.ui.massive.images img,.ui.massive.images svg{width:960px;height:auto;font-size:1.71428571rem}.ui.images{font-size:0;margin:0 -.25rem 0}.ui.images .image,.ui.images img,.ui.images svg{display:inline-block;margin:0 .25rem .5rem} -------------------------------------------------------------------------------- /public/css/components/container.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Container 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Container 14 | *******************************/ 15 | 16 | 17 | /* All Sizes */ 18 | .ui.container { 19 | display: block; 20 | max-width: 100% !important; 21 | } 22 | 23 | /* Mobile */ 24 | @media only screen and (max-width: 767px) { 25 | .ui.container { 26 | width: auto !important; 27 | margin-left: 1em !important; 28 | margin-right: 1em !important; 29 | } 30 | .ui.grid.container { 31 | width: auto !important; 32 | } 33 | .ui.relaxed.grid.container { 34 | width: auto !important; 35 | } 36 | .ui.very.relaxed.grid.container { 37 | width: auto !important; 38 | } 39 | } 40 | 41 | /* Tablet */ 42 | @media only screen and (min-width: 768px) and (max-width: 991px) { 43 | .ui.container { 44 | width: 723px; 45 | margin-left: auto !important; 46 | margin-right: auto !important; 47 | } 48 | .ui.grid.container { 49 | width: calc( 723px + 2rem ) !important; 50 | } 51 | .ui.relaxed.grid.container { 52 | width: calc( 723px + 3rem ) !important; 53 | } 54 | .ui.very.relaxed.grid.container { 55 | width: calc( 723px + 5rem ) !important; 56 | } 57 | } 58 | 59 | /* Small Monitor */ 60 | @media only screen and (min-width: 992px) and (max-width: 1199px) { 61 | .ui.container { 62 | width: 933px; 63 | margin-left: auto !important; 64 | margin-right: auto !important; 65 | } 66 | .ui.grid.container { 67 | width: calc( 933px + 2rem ) !important; 68 | } 69 | .ui.relaxed.grid.container { 70 | width: calc( 933px + 3rem ) !important; 71 | } 72 | .ui.very.relaxed.grid.container { 73 | width: calc( 933px + 5rem ) !important; 74 | } 75 | } 76 | 77 | /* Large Monitor */ 78 | @media only screen and (min-width: 1200px) { 79 | .ui.container { 80 | width: 1127px; 81 | margin-left: auto !important; 82 | margin-right: auto !important; 83 | } 84 | .ui.grid.container { 85 | width: calc( 1127px + 2rem ) !important; 86 | } 87 | .ui.relaxed.grid.container { 88 | width: calc( 1127px + 3rem ) !important; 89 | } 90 | .ui.very.relaxed.grid.container { 91 | width: calc( 1127px + 5rem ) !important; 92 | } 93 | } 94 | 95 | 96 | /******************************* 97 | Types 98 | *******************************/ 99 | 100 | 101 | /* Text Container */ 102 | .ui.text.container { 103 | font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; 104 | max-width: 700px !important; 105 | line-height: 1.5; 106 | } 107 | .ui.text.container { 108 | font-size: 1.14285714rem; 109 | } 110 | 111 | /* Fluid */ 112 | .ui.fluid.container { 113 | width: 100%; 114 | } 115 | 116 | 117 | /******************************* 118 | Variations 119 | *******************************/ 120 | 121 | .ui[class*="left aligned"].container { 122 | text-align: left; 123 | } 124 | .ui[class*="center aligned"].container { 125 | text-align: center; 126 | } 127 | .ui[class*="right aligned"].container { 128 | text-align: right; 129 | } 130 | .ui.justified.container { 131 | text-align: justify; 132 | -webkit-hyphens: auto; 133 | -ms-hyphens: auto; 134 | hyphens: auto; 135 | } 136 | 137 | 138 | /******************************* 139 | Theme Overrides 140 | *******************************/ 141 | 142 | 143 | 144 | /******************************* 145 | Site Overrides 146 | *******************************/ 147 | 148 | -------------------------------------------------------------------------------- /models/posts.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 与文章相关的操作 4 | */ 5 | let posts = require('../lib/mongo').Post; 6 | let CommentModel = require('./comments'); 7 | const marked = require('marked'); 8 | 9 | //将post的content 从 markdown 转换成 html 10 | posts.plugin('contentToHtml',{ 11 | afterFind: function(posts){ 12 | return posts.map(function (post){ 13 | post.content = marked(post.content); 14 | return post; 15 | }); 16 | }, 17 | afterFindOne: function(post){ 18 | if(post){ 19 | post.content = marked(post.content); 20 | } 21 | return post; 22 | } 23 | }); 24 | 25 | //给post添加留言数 commentsCount 26 | posts.plugin('addCommentsCount',{ 27 | afterFind: function(posts){ 28 | return Promise.all(posts.map(function(post){ 29 | return CommentModel.getCommentsCount(post._id).then(function (commentsCount){ 30 | post.commentsCount = commentsCount; 31 | return post; 32 | }); 33 | })); 34 | }, 35 | afterFindOne: function(post){ 36 | if(post){ 37 | return CommentModel.getCommentsCount(post._id).then(function(count){ 38 | post.commentsCount = count; 39 | return post; 40 | }); 41 | } 42 | return post; 43 | } 44 | }); 45 | 46 | module.exports = { 47 | //创建一篇文章 48 | create : function create(post){ 49 | return posts.create(post).exec(); 50 | }, 51 | 52 | //通过文章id来获取文章 53 | getPostById: function getPostById(postId){ 54 | return posts.findOne({_id:postId}) 55 | .populate({path:'author',model:'User'}) //联表查询(通过path,表是User) 56 | .addCreatedAt() //创建时间插件 57 | .addCommentsCount() 58 | .contentToHtml() //markdown内容转化为html 59 | .exec(); 60 | }, 61 | 62 | //按创建时间降序获取所有用户文章或者某个特定用户的所有文章 63 | getPosts : function getPosts(author){ 64 | let query = {}; 65 | //如果query为空,查询所有用户的文章,否则查询指定用户的文章 66 | if(query){ 67 | query.author = author; 68 | } 69 | console.log(query); 70 | if(query.author === undefined){ 71 | query = {}; 72 | } 73 | return posts.find(query) 74 | .populate({path:'author',model:'User'}) 75 | .sort({_id: -1}) //倒序 76 | .addCreatedAt() 77 | .addCommentsCount() 78 | .contentToHtml() 79 | .exec(); 80 | }, 81 | 82 | //通过文章id 给pv 加1 83 | incPv : function incPv(postId){ 84 | return posts.update({_id:postId},{$inc:{pv: 1}}) 85 | .exec(); 86 | }, 87 | 88 | //通过文章id 获取一篇原生文章 89 | getRawPostById: function getRawPostById(postId){ 90 | return posts.findOne({_id:postId}) 91 | .populate({path:'author',model:'User'}) 92 | .exec(); 93 | }, 94 | 95 | //通过用户id 和文章 id 更新一篇文章 96 | updatePostById: function updatePostById(postId,author,data){ 97 | return posts.update({author:author,_id:postId},{$set: data}).exec(); 98 | }, 99 | 100 | //通过用户id和文章id删除一篇文章 101 | delPostById: function delPostById(postId,author){ 102 | return posts.remove({author:author,_id:postId}) 103 | .exec() 104 | .then(function (res){ 105 | //文章删除后,再删除该文章下的所有留言 106 | if(res.result.ok && res.result.n > 0){ 107 | return CommentModel.delCommentsByPostId(postId); 108 | } 109 | }); 110 | }, 111 | 112 | }; 113 | -------------------------------------------------------------------------------- /public/css/components/embed.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Video 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Types 14 | *******************************/ 15 | 16 | .ui.embed { 17 | position: relative; 18 | max-width: 100%; 19 | height: 0px; 20 | overflow: hidden; 21 | background: #DCDDDE; 22 | padding-bottom: 56.25%; 23 | } 24 | 25 | /*----------------- 26 | Embedded Content 27 | ------------------*/ 28 | 29 | .ui.embed iframe, 30 | .ui.embed embed, 31 | .ui.embed object { 32 | position: absolute; 33 | border: none; 34 | width: 100%; 35 | height: 100%; 36 | top: 0px; 37 | left: 0px; 38 | margin: 0em; 39 | padding: 0em; 40 | } 41 | 42 | /*----------------- 43 | Embed 44 | ------------------*/ 45 | 46 | .ui.embed > .embed { 47 | display: none; 48 | } 49 | 50 | /*-------------- 51 | Placeholder 52 | ---------------*/ 53 | 54 | .ui.embed > .placeholder { 55 | position: absolute; 56 | cursor: pointer; 57 | top: 0px; 58 | left: 0px; 59 | display: block; 60 | width: 100%; 61 | height: 100%; 62 | background-color: radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3)); 63 | } 64 | 65 | /*-------------- 66 | Icon 67 | ---------------*/ 68 | 69 | .ui.embed > .icon { 70 | cursor: pointer; 71 | position: absolute; 72 | top: 0px; 73 | left: 0px; 74 | width: 100%; 75 | height: 100%; 76 | z-index: 2; 77 | } 78 | .ui.embed > .icon:after { 79 | position: absolute; 80 | top: 0%; 81 | left: 0%; 82 | width: 100%; 83 | height: 100%; 84 | z-index: 3; 85 | content: ''; 86 | background: -webkit-radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3)); 87 | background: radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3)); 88 | opacity: 0.5; 89 | -webkit-transition: opacity 0.5s ease; 90 | transition: opacity 0.5s ease; 91 | } 92 | .ui.embed > .icon:before { 93 | position: absolute; 94 | top: 50%; 95 | left: 50%; 96 | z-index: 4; 97 | -webkit-transform: translateX(-50%) translateY(-50%); 98 | transform: translateX(-50%) translateY(-50%); 99 | color: #FFFFFF; 100 | font-size: 6rem; 101 | text-shadow: 0px 2px 10px rgba(34, 36, 38, 0.2); 102 | -webkit-transition: opacity 0.5s ease, color 0.5s ease; 103 | transition: opacity 0.5s ease, color 0.5s ease; 104 | z-index: 10; 105 | } 106 | 107 | 108 | /******************************* 109 | States 110 | *******************************/ 111 | 112 | 113 | /*-------------- 114 | Hover 115 | ---------------*/ 116 | 117 | .ui.embed .icon:hover:after { 118 | background: -webkit-radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3)); 119 | background: radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3)); 120 | opacity: 1; 121 | } 122 | .ui.embed .icon:hover:before { 123 | color: #FFFFFF; 124 | } 125 | 126 | /*-------------- 127 | Active 128 | ---------------*/ 129 | 130 | .ui.active.embed > .icon, 131 | .ui.active.embed > .placeholder { 132 | display: none; 133 | } 134 | .ui.active.embed > .embed { 135 | display: block; 136 | } 137 | 138 | 139 | /******************************* 140 | Video Overrides 141 | *******************************/ 142 | 143 | 144 | 145 | /******************************* 146 | Site Overrides 147 | *******************************/ 148 | 149 | 150 | 151 | /******************************* 152 | Variations 153 | *******************************/ 154 | 155 | .ui.square.embed { 156 | padding-bottom: 100%; 157 | } 158 | .ui[class*="4:3"].embed { 159 | padding-bottom: 75%; 160 | } 161 | .ui[class*="16:9"].embed { 162 | padding-bottom: 56.25%; 163 | } 164 | .ui[class*="21:9"].embed { 165 | padding-bottom: 42.85714286%; 166 | } 167 | -------------------------------------------------------------------------------- /public/css/components/shape.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Shape 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Shape 14 | *******************************/ 15 | 16 | .ui.shape { 17 | position: relative; 18 | vertical-align: top; 19 | display: inline-block; 20 | -webkit-perspective: 2000px; 21 | perspective: 2000px; 22 | -webkit-transition: left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; 23 | transition: left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; 24 | transition: transform 0.6s ease-in-out, left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out; 25 | transition: transform 0.6s ease-in-out, left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; 26 | } 27 | .ui.shape .sides { 28 | -webkit-transform-style: preserve-3d; 29 | transform-style: preserve-3d; 30 | } 31 | .ui.shape .side { 32 | opacity: 1; 33 | width: 100%; 34 | margin: 0em !important; 35 | -webkit-backface-visibility: hidden; 36 | backface-visibility: hidden; 37 | } 38 | .ui.shape .side { 39 | display: none; 40 | } 41 | .ui.shape .side * { 42 | -webkit-backface-visibility: visible !important; 43 | backface-visibility: visible !important; 44 | } 45 | 46 | 47 | /******************************* 48 | Types 49 | *******************************/ 50 | 51 | .ui.cube.shape .side { 52 | min-width: 15em; 53 | height: 15em; 54 | padding: 2em; 55 | background-color: #E6E6E6; 56 | color: rgba(0, 0, 0, 0.87); 57 | box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.3); 58 | } 59 | .ui.cube.shape .side > .content { 60 | width: 100%; 61 | height: 100%; 62 | display: table; 63 | text-align: center; 64 | -webkit-user-select: text; 65 | -moz-user-select: text; 66 | -ms-user-select: text; 67 | user-select: text; 68 | } 69 | .ui.cube.shape .side > .content > div { 70 | display: table-cell; 71 | vertical-align: middle; 72 | font-size: 2em; 73 | } 74 | 75 | 76 | /******************************* 77 | Variations 78 | *******************************/ 79 | 80 | .ui.text.shape.animating .sides { 81 | position: static; 82 | } 83 | .ui.text.shape .side { 84 | white-space: nowrap; 85 | } 86 | .ui.text.shape .side > * { 87 | white-space: normal; 88 | } 89 | 90 | 91 | /******************************* 92 | States 93 | *******************************/ 94 | 95 | 96 | /*-------------- 97 | Loading 98 | ---------------*/ 99 | 100 | .ui.loading.shape { 101 | position: absolute; 102 | top: -9999px; 103 | left: -9999px; 104 | } 105 | 106 | /*-------------- 107 | Animating 108 | ---------------*/ 109 | 110 | .ui.shape .animating.side { 111 | position: absolute; 112 | top: 0px; 113 | left: 0px; 114 | display: block; 115 | z-index: 100; 116 | } 117 | .ui.shape .hidden.side { 118 | opacity: 0.6; 119 | } 120 | 121 | /*-------------- 122 | CSS 123 | ---------------*/ 124 | 125 | .ui.shape.animating .sides { 126 | position: absolute; 127 | } 128 | .ui.shape.animating .sides { 129 | -webkit-transition: left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; 130 | transition: left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; 131 | transition: transform 0.6s ease-in-out, left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out; 132 | transition: transform 0.6s ease-in-out, left 0.6s ease-in-out, width 0.6s ease-in-out, height 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; 133 | } 134 | .ui.shape.animating .side { 135 | -webkit-transition: opacity 0.6s ease-in-out; 136 | transition: opacity 0.6s ease-in-out; 137 | } 138 | 139 | /*-------------- 140 | Active 141 | ---------------*/ 142 | 143 | .ui.shape .active.side { 144 | display: block; 145 | } 146 | 147 | 148 | /******************************* 149 | Theme Overrides 150 | *******************************/ 151 | 152 | 153 | 154 | /******************************* 155 | User Overrides 156 | *******************************/ 157 | 158 | -------------------------------------------------------------------------------- /public/css/components/loader.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Loader 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.loader{display:none;position:absolute;top:50%;left:50%;margin:0;text-align:center;z-index:1000;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.ui.loader:before{position:absolute;content:'';top:0;left:50%;width:100%;height:100%;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loader:after{position:absolute;content:'';top:0;left:50%;width:100%;height:100%;-webkit-animation:loader .6s linear;animation:loader .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#767676 transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent}@-webkit-keyframes loader{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loader{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.ui.mini.loader:after,.ui.mini.loader:before{width:1rem;height:1rem;margin:0 0 0 -.5rem}.ui.tiny.loader:after,.ui.tiny.loader:before{width:1.14285714rem;height:1.14285714rem;margin:0 0 0 -.57142857rem}.ui.small.loader:after,.ui.small.loader:before{width:1.71428571rem;height:1.71428571rem;margin:0 0 0 -.85714286rem}.ui.loader:after,.ui.loader:before{width:2.28571429rem;height:2.28571429rem;margin:0 0 0 -1.14285714rem}.ui.large.loader:after,.ui.large.loader:before{width:3.42857143rem;height:3.42857143rem;margin:0 0 0 -1.71428571rem}.ui.big.loader:after,.ui.big.loader:before{width:3.71428571rem;height:3.71428571rem;margin:0 0 0 -1.85714286rem}.ui.huge.loader:after,.ui.huge.loader:before{width:4.14285714rem;height:4.14285714rem;margin:0 0 0 -2.07142857rem}.ui.massive.loader:after,.ui.massive.loader:before{width:4.57142857rem;height:4.57142857rem;margin:0 0 0 -2.28571429rem}.ui.dimmer .loader{display:block}.ui.dimmer .ui.loader{color:rgba(255,255,255,.9)}.ui.dimmer .ui.loader:before{border-color:rgba(255,255,255,.15)}.ui.dimmer .ui.loader:after{border-color:#fff transparent transparent}.ui.inverted.dimmer .ui.loader{color:rgba(0,0,0,.87)}.ui.inverted.dimmer .ui.loader:before{border-color:rgba(0,0,0,.1)}.ui.inverted.dimmer .ui.loader:after{border-color:#767676 transparent transparent}.ui.text.loader{width:auto!important;height:auto!important;text-align:center;font-style:normal}.ui.indeterminate.loader:after{-webkit-animation-direction:reverse;animation-direction:reverse;-webkit-animation-duration:1.2s;animation-duration:1.2s}.ui.loader.active,.ui.loader.visible{display:block}.ui.loader.disabled,.ui.loader.hidden{display:none}.ui.inverted.dimmer .ui.mini.loader,.ui.mini.loader{width:1rem;height:1rem;font-size:.78571429em}.ui.inverted.dimmer .ui.tiny.loader,.ui.tiny.loader{width:1.14285714rem;height:1.14285714rem;font-size:.85714286em}.ui.inverted.dimmer .ui.small.loader,.ui.small.loader{width:1.71428571rem;height:1.71428571rem;font-size:.92857143em}.ui.inverted.dimmer .ui.loader,.ui.loader{width:2.28571429rem;height:2.28571429rem;font-size:1em}.ui.inverted.dimmer .ui.large.loader,.ui.large.loader{width:3.42857143rem;height:3.42857143rem;font-size:1.14285714em}.ui.big.loader,.ui.inverted.dimmer .ui.big.loader{width:3.71428571rem;height:3.71428571rem;font-size:1.28571429em}.ui.huge.loader,.ui.inverted.dimmer .ui.huge.loader{width:4.14285714rem;height:4.14285714rem;font-size:1.42857143em}.ui.inverted.dimmer .ui.massive.loader,.ui.massive.loader{width:4.57142857rem;height:4.57142857rem;font-size:1.71428571em}.ui.mini.text.loader{min-width:1rem;padding-top:1.78571429rem}.ui.tiny.text.loader{min-width:1.14285714rem;padding-top:1.92857143rem}.ui.small.text.loader{min-width:1.71428571rem;padding-top:2.5rem}.ui.text.loader{min-width:2.28571429rem;padding-top:3.07142857rem}.ui.large.text.loader{min-width:3.42857143rem;padding-top:4.21428571rem}.ui.big.text.loader{min-width:3.71428571rem;padding-top:4.5rem}.ui.huge.text.loader{min-width:4.14285714rem;padding-top:4.92857143rem}.ui.massive.text.loader{min-width:4.57142857rem;padding-top:5.35714286rem}.ui.inverted.loader{color:rgba(255,255,255,.9)}.ui.inverted.loader:before{border-color:rgba(255,255,255,.15)}.ui.inverted.loader:after{border-top-color:#fff}.ui.inline.loader{position:relative;vertical-align:middle;margin:0;left:0;top:0;-webkit-transform:none;transform:none}.ui.inline.loader.active,.ui.inline.loader.visible{display:inline-block}.ui.centered.inline.loader.active,.ui.centered.inline.loader.visible{display:block;margin-left:auto;margin-right:auto} -------------------------------------------------------------------------------- /public/css/components/dimmer.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Dimmer 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Dimmer 14 | *******************************/ 15 | 16 | .dimmable:not(.body) { 17 | position: relative; 18 | } 19 | .ui.dimmer { 20 | display: none; 21 | position: absolute; 22 | top: 0em !important; 23 | left: 0em !important; 24 | width: 100%; 25 | height: 100%; 26 | text-align: center; 27 | vertical-align: middle; 28 | background-color: rgba(0, 0, 0, 0.85); 29 | opacity: 0; 30 | line-height: 1; 31 | -webkit-animation-fill-mode: both; 32 | animation-fill-mode: both; 33 | -webkit-animation-duration: 0.5s; 34 | animation-duration: 0.5s; 35 | -webkit-transition: background-color 0.5s linear; 36 | transition: background-color 0.5s linear; 37 | -webkit-user-select: none; 38 | -moz-user-select: none; 39 | -ms-user-select: none; 40 | user-select: none; 41 | will-change: opacity; 42 | z-index: 1000; 43 | } 44 | 45 | /* Dimmer Content */ 46 | .ui.dimmer > .content { 47 | width: 100%; 48 | height: 100%; 49 | display: table; 50 | -webkit-user-select: text; 51 | -moz-user-select: text; 52 | -ms-user-select: text; 53 | user-select: text; 54 | } 55 | .ui.dimmer > .content > * { 56 | display: table-cell; 57 | vertical-align: middle; 58 | color: #FFFFFF; 59 | } 60 | 61 | /* Loose Coupling */ 62 | .ui.segment > .ui.dimmer { 63 | border-radius: inherit !important; 64 | } 65 | 66 | 67 | /******************************* 68 | States 69 | *******************************/ 70 | 71 | .animating.dimmable:not(body), 72 | .dimmed.dimmable:not(body) { 73 | overflow: hidden; 74 | } 75 | .dimmed.dimmable > .ui.animating.dimmer, 76 | .dimmed.dimmable > .ui.visible.dimmer, 77 | .ui.active.dimmer { 78 | display: block; 79 | opacity: 1; 80 | } 81 | .ui.disabled.dimmer { 82 | width: 0 !important; 83 | height: 0 !important; 84 | } 85 | 86 | 87 | /******************************* 88 | Variations 89 | *******************************/ 90 | 91 | 92 | /*-------------- 93 | Page 94 | ---------------*/ 95 | 96 | .ui.page.dimmer { 97 | position: fixed; 98 | -webkit-transform-style: ''; 99 | transform-style: ''; 100 | -webkit-perspective: 2000px; 101 | perspective: 2000px; 102 | -webkit-transform-origin: center center; 103 | transform-origin: center center; 104 | } 105 | body.animating.in.dimmable, 106 | body.dimmed.dimmable { 107 | overflow: hidden; 108 | } 109 | body.dimmable > .dimmer { 110 | position: fixed; 111 | } 112 | 113 | /*-------------- 114 | Blurring 115 | ---------------*/ 116 | 117 | .blurring.dimmable > :not(.dimmer) { 118 | -webkit-filter: blur(0px) grayscale(0); 119 | filter: blur(0px) grayscale(0); 120 | -webkit-transition: 800ms -webkit-filter ease; 121 | transition: 800ms -webkit-filter ease; 122 | transition: 800ms filter ease; 123 | transition: 800ms filter ease, 800ms -webkit-filter ease; 124 | } 125 | .blurring.dimmed.dimmable > :not(.dimmer) { 126 | -webkit-filter: blur(5px) grayscale(0.7); 127 | filter: blur(5px) grayscale(0.7); 128 | } 129 | 130 | /* Dimmer Color */ 131 | .blurring.dimmable > .dimmer { 132 | background-color: rgba(0, 0, 0, 0.6); 133 | } 134 | .blurring.dimmable > .inverted.dimmer { 135 | background-color: rgba(255, 255, 255, 0.6); 136 | } 137 | 138 | /*-------------- 139 | Aligned 140 | ---------------*/ 141 | 142 | .ui.dimmer > .top.aligned.content > * { 143 | vertical-align: top; 144 | } 145 | .ui.dimmer > .bottom.aligned.content > * { 146 | vertical-align: bottom; 147 | } 148 | 149 | /*-------------- 150 | Inverted 151 | ---------------*/ 152 | 153 | .ui.inverted.dimmer { 154 | background-color: rgba(255, 255, 255, 0.85); 155 | } 156 | .ui.inverted.dimmer > .content > * { 157 | color: #FFFFFF; 158 | } 159 | 160 | /*-------------- 161 | Simple 162 | ---------------*/ 163 | 164 | 165 | /* Displays without javascript */ 166 | .ui.simple.dimmer { 167 | display: block; 168 | overflow: hidden; 169 | opacity: 1; 170 | width: 0%; 171 | height: 0%; 172 | z-index: -100; 173 | background-color: rgba(0, 0, 0, 0); 174 | } 175 | .dimmed.dimmable > .ui.simple.dimmer { 176 | overflow: visible; 177 | opacity: 1; 178 | width: 100%; 179 | height: 100%; 180 | background-color: rgba(0, 0, 0, 0.85); 181 | z-index: 1; 182 | } 183 | .ui.simple.inverted.dimmer { 184 | background-color: rgba(255, 255, 255, 0); 185 | } 186 | .dimmed.dimmable > .ui.simple.inverted.dimmer { 187 | background-color: rgba(255, 255, 255, 0.85); 188 | } 189 | 190 | 191 | /******************************* 192 | Theme Overrides 193 | *******************************/ 194 | 195 | 196 | 197 | /******************************* 198 | User Overrides 199 | *******************************/ 200 | 201 | -------------------------------------------------------------------------------- /public/css/components/ad.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Ad 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Copyright 2013 Contributors 7 | * Released under the MIT license 8 | * http://opensource.org/licenses/MIT 9 | * 10 | */ 11 | 12 | 13 | /******************************* 14 | Advertisement 15 | *******************************/ 16 | 17 | .ui.ad { 18 | display: block; 19 | overflow: hidden; 20 | margin: 1em 0em; 21 | } 22 | .ui.ad:first-child { 23 | margin: 0em; 24 | } 25 | .ui.ad:last-child { 26 | margin: 0em; 27 | } 28 | .ui.ad iframe { 29 | margin: 0em; 30 | padding: 0em; 31 | border: none; 32 | overflow: hidden; 33 | } 34 | 35 | /*-------------- 36 | Common 37 | ---------------*/ 38 | 39 | 40 | /* Leaderboard */ 41 | .ui.leaderboard.ad { 42 | width: 728px; 43 | height: 90px; 44 | } 45 | 46 | /* Medium Rectangle */ 47 | .ui[class*="medium rectangle"].ad { 48 | width: 300px; 49 | height: 250px; 50 | } 51 | 52 | /* Large Rectangle */ 53 | .ui[class*="large rectangle"].ad { 54 | width: 336px; 55 | height: 280px; 56 | } 57 | 58 | /* Half Page */ 59 | .ui[class*="half page"].ad { 60 | width: 300px; 61 | height: 600px; 62 | } 63 | 64 | /*-------------- 65 | Square 66 | ---------------*/ 67 | 68 | 69 | /* Square */ 70 | .ui.square.ad { 71 | width: 250px; 72 | height: 250px; 73 | } 74 | 75 | /* Small Square */ 76 | .ui[class*="small square"].ad { 77 | width: 200px; 78 | height: 200px; 79 | } 80 | 81 | /*-------------- 82 | Rectangle 83 | ---------------*/ 84 | 85 | 86 | /* Small Rectangle */ 87 | .ui[class*="small rectangle"].ad { 88 | width: 180px; 89 | height: 150px; 90 | } 91 | 92 | /* Vertical Rectangle */ 93 | .ui[class*="vertical rectangle"].ad { 94 | width: 240px; 95 | height: 400px; 96 | } 97 | 98 | /*-------------- 99 | Button 100 | ---------------*/ 101 | 102 | .ui.button.ad { 103 | width: 120px; 104 | height: 90px; 105 | } 106 | .ui[class*="square button"].ad { 107 | width: 125px; 108 | height: 125px; 109 | } 110 | .ui[class*="small button"].ad { 111 | width: 120px; 112 | height: 60px; 113 | } 114 | 115 | /*-------------- 116 | Skyscrapers 117 | ---------------*/ 118 | 119 | 120 | /* Skyscraper */ 121 | .ui.skyscraper.ad { 122 | width: 120px; 123 | height: 600px; 124 | } 125 | 126 | /* Wide Skyscraper */ 127 | .ui[class*="wide skyscraper"].ad { 128 | width: 160px; 129 | } 130 | 131 | /*-------------- 132 | Banners 133 | ---------------*/ 134 | 135 | 136 | /* Banner */ 137 | .ui.banner.ad { 138 | width: 468px; 139 | height: 60px; 140 | } 141 | 142 | /* Vertical Banner */ 143 | .ui[class*="vertical banner"].ad { 144 | width: 120px; 145 | height: 240px; 146 | } 147 | 148 | /* Top Banner */ 149 | .ui[class*="top banner"].ad { 150 | width: 930px; 151 | height: 180px; 152 | } 153 | 154 | /* Half Banner */ 155 | .ui[class*="half banner"].ad { 156 | width: 234px; 157 | height: 60px; 158 | } 159 | 160 | /*-------------- 161 | Boards 162 | ---------------*/ 163 | 164 | 165 | /* Leaderboard */ 166 | .ui[class*="large leaderboard"].ad { 167 | width: 970px; 168 | height: 90px; 169 | } 170 | 171 | /* Billboard */ 172 | .ui.billboard.ad { 173 | width: 970px; 174 | height: 250px; 175 | } 176 | 177 | /*-------------- 178 | Panorama 179 | ---------------*/ 180 | 181 | 182 | /* Panorama */ 183 | .ui.panorama.ad { 184 | width: 980px; 185 | height: 120px; 186 | } 187 | 188 | /*-------------- 189 | Netboard 190 | ---------------*/ 191 | 192 | 193 | /* Netboard */ 194 | .ui.netboard.ad { 195 | width: 580px; 196 | height: 400px; 197 | } 198 | 199 | /*-------------- 200 | Mobile 201 | ---------------*/ 202 | 203 | 204 | /* Large Mobile Banner */ 205 | .ui[class*="large mobile banner"].ad { 206 | width: 320px; 207 | height: 100px; 208 | } 209 | 210 | /* Mobile Leaderboard */ 211 | .ui[class*="mobile leaderboard"].ad { 212 | width: 320px; 213 | height: 50px; 214 | } 215 | 216 | 217 | /******************************* 218 | Types 219 | *******************************/ 220 | 221 | 222 | /* Mobile Sizes */ 223 | .ui.mobile.ad { 224 | display: none; 225 | } 226 | @media only screen and (max-width: 767px) { 227 | .ui.mobile.ad { 228 | display: block; 229 | } 230 | } 231 | 232 | 233 | /******************************* 234 | Variations 235 | *******************************/ 236 | 237 | .ui.centered.ad { 238 | margin-left: auto; 239 | margin-right: auto; 240 | } 241 | .ui.test.ad { 242 | position: relative; 243 | background: #545454; 244 | } 245 | .ui.test.ad:after { 246 | position: absolute; 247 | top: 50%; 248 | left: 50%; 249 | width: 100%; 250 | text-align: center; 251 | -webkit-transform: translateX(-50%) translateY(-50%); 252 | transform: translateX(-50%) translateY(-50%); 253 | content: 'Ad'; 254 | color: #FFFFFF; 255 | font-size: 1em; 256 | font-weight: bold; 257 | } 258 | .ui.mobile.test.ad:after { 259 | font-size: 0.85714286em; 260 | } 261 | .ui.test.ad[data-text]:after { 262 | content: attr(data-text); 263 | } 264 | 265 | 266 | /******************************* 267 | Theme Overrides 268 | *******************************/ 269 | 270 | 271 | 272 | /******************************* 273 | User Variable Overrides 274 | *******************************/ 275 | 276 | -------------------------------------------------------------------------------- /public/css/components/rating.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Rating 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | !function(e,n,t,i){"use strict";n="undefined"!=typeof n&&n.Math==Math?n:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.rating=function(n){var t,a=e(this),o=a.selector||"",r=(new Date).getTime(),s=[],l=arguments[0],c="string"==typeof l,u=[].slice.call(arguments,1);return a.each(function(){var d,g,m=e.isPlainObject(n)?e.extend(!0,{},e.fn.rating.settings,n):e.extend({},e.fn.rating.settings),f=m.namespace,v=m.className,p=m.metadata,b=m.selector,h=(m.error,"."+f),y="module-"+f,x=this,R=e(this).data(y),C=e(this),T=C.find(b.icon);g={initialize:function(){g.verbose("Initializing rating module",m),0===T.length&&g.setup.layout(),m.interactive?g.enable():g.disable(),g.set.initialLoad(),g.set.rating(g.get.initialRating()),g.remove.initialLoad(),g.instantiate()},instantiate:function(){g.verbose("Instantiating module",m),R=g,C.data(y,g)},destroy:function(){g.verbose("Destroying previous instance",R),g.remove.events(),C.removeData(y)},refresh:function(){T=C.find(b.icon)},setup:{layout:function(){var n=g.get.maxRating(),t=e.fn.rating.settings.templates.icon(n);g.debug("Generating icon html dynamically"),C.html(t),g.refresh()}},event:{mouseenter:function(){var n=e(this);n.nextAll().removeClass(v.selected),C.addClass(v.selected),n.addClass(v.selected).prevAll().addClass(v.selected)},mouseleave:function(){C.removeClass(v.selected),T.removeClass(v.selected)},click:function(){var n=e(this),t=g.get.rating(),i=T.index(n)+1,a="auto"==m.clearable?1===T.length:m.clearable;a&&t==i?g.clearRating():g.set.rating(i)}},clearRating:function(){g.debug("Clearing current rating"),g.set.rating(0)},bind:{events:function(){g.verbose("Binding events"),C.on("mouseenter"+h,b.icon,g.event.mouseenter).on("mouseleave"+h,b.icon,g.event.mouseleave).on("click"+h,b.icon,g.event.click)}},remove:{events:function(){g.verbose("Removing events"),C.off(h)},initialLoad:function(){d=!1}},enable:function(){g.debug("Setting rating to interactive mode"),g.bind.events(),C.removeClass(v.disabled)},disable:function(){g.debug("Setting rating to read-only mode"),g.remove.events(),C.addClass(v.disabled)},is:{initialLoad:function(){return d}},get:{initialRating:function(){return C.data(p.rating)!==i?(C.removeData(p.rating),C.data(p.rating)):m.initialRating},maxRating:function(){return C.data(p.maxRating)!==i?(C.removeData(p.maxRating),C.data(p.maxRating)):m.maxRating},rating:function(){var e=T.filter("."+v.active).length;return g.verbose("Current rating retrieved",e),e}},set:{rating:function(e){var n=e-1>=0?e-1:0,t=T.eq(n);C.removeClass(v.selected),T.removeClass(v.selected).removeClass(v.active),e>0&&(g.verbose("Setting current rating to",e),t.prevAll().addBack().addClass(v.active)),g.is.initialLoad()||m.onRate.call(x,e)},initialLoad:function(){d=!0}},setting:function(n,t){if(g.debug("Changing setting",n,t),e.isPlainObject(n))e.extend(!0,m,n);else{if(t===i)return m[n];e.isPlainObject(m[n])?e.extend(!0,m[n],t):m[n]=t}},internal:function(n,t){if(e.isPlainObject(n))e.extend(!0,g,n);else{if(t===i)return g[n];g[n]=t}},debug:function(){!m.silent&&m.debug&&(m.performance?g.performance.log(arguments):(g.debug=Function.prototype.bind.call(console.info,console,m.name+":"),g.debug.apply(console,arguments)))},verbose:function(){!m.silent&&m.verbose&&m.debug&&(m.performance?g.performance.log(arguments):(g.verbose=Function.prototype.bind.call(console.info,console,m.name+":"),g.verbose.apply(console,arguments)))},error:function(){m.silent||(g.error=Function.prototype.bind.call(console.error,console,m.name+":"),g.error.apply(console,arguments))},performance:{log:function(e){var n,t,i;m.performance&&(n=(new Date).getTime(),i=r||n,t=n-i,r=n,s.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:x,"Execution Time":t})),clearTimeout(g.performance.timer),g.performance.timer=setTimeout(g.performance.display,500)},display:function(){var n=m.name+":",t=0;r=!1,clearTimeout(g.performance.timer),e.each(s,function(e,n){t+=n["Execution Time"]}),n+=" "+t+"ms",o&&(n+=" '"+o+"'"),a.length>1&&(n+=" ("+a.length+")"),(console.group!==i||console.table!==i)&&s.length>0&&(console.groupCollapsed(n),console.table?console.table(s):e.each(s,function(e,n){console.log(n.Name+": "+n["Execution Time"]+"ms")}),console.groupEnd()),s=[]}},invoke:function(n,a,o){var r,s,l,c=R;return a=a||u,o=x||o,"string"==typeof n&&c!==i&&(n=n.split(/[\. ]/),r=n.length-1,e.each(n,function(t,a){var o=t!=r?a+n[t+1].charAt(0).toUpperCase()+n[t+1].slice(1):n;if(e.isPlainObject(c[o])&&t!=r)c=c[o];else{if(c[o]!==i)return s=c[o],!1;if(!e.isPlainObject(c[a])||t==r)return c[a]!==i&&(s=c[a],!1);c=c[a]}})),e.isFunction(s)?l=s.apply(o,a):s!==i&&(l=s),e.isArray(t)?t.push(l):t!==i?t=[t,l]:l!==i&&(t=l),s}},c?(R===i&&g.initialize(),g.invoke(l)):(R!==i&&R.invoke("destroy"),g.initialize())}),t!==i?t:this},e.fn.rating.settings={name:"Rating",namespace:"rating",slent:!1,debug:!1,verbose:!1,performance:!0,initialRating:0,interactive:!0,maxRating:4,clearable:"auto",fireOnInit:!1,onRate:function(e){},error:{method:"The method you called is not defined",noMaximum:"No maximum rating specified. Cannot generate HTML automatically"},metadata:{rating:"rating",maxRating:"maxRating"},className:{active:"active",disabled:"disabled",selected:"selected",loading:"loading"},selector:{icon:".icon"},templates:{icon:function(e){for(var n=1,t="";n<=e;)t+='',n++;return t}}}}(jQuery,window,document); -------------------------------------------------------------------------------- /public/css/components/reveal.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Reveal 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.reveal{display:inherit;position:relative!important;font-size:0!important}.ui.reveal>.visible.content{position:absolute!important;top:0!important;left:0!important;z-index:3!important;-webkit-transition:all .5s ease .1s;transition:all .5s ease .1s}.ui.reveal>.hidden.content{position:relative!important;z-index:2!important}.ui.active.reveal .visible.content,.ui.reveal:hover .visible.content{z-index:4!important}.ui.slide.reveal{position:relative!important;overflow:hidden!important;white-space:nowrap}.ui.slide.reveal>.content{display:block;width:100%;float:left;margin:0;-webkit-transition:-webkit-transform .5s ease .1s;transition:-webkit-transform .5s ease .1s;transition:transform .5s ease .1s;transition:transform .5s ease .1s,-webkit-transform .5s ease .1s}.ui.slide.reveal>.visible.content{position:relative!important}.ui.slide.reveal>.hidden.content{position:absolute!important;left:0!important;width:100%!important;-webkit-transform:translateX(100%)!important;transform:translateX(100%)!important}.ui.slide.active.reveal>.visible.content,.ui.slide.reveal:hover>.visible.content{-webkit-transform:translateX(-100%)!important;transform:translateX(-100%)!important}.ui.slide.active.reveal>.hidden.content,.ui.slide.reveal:hover>.hidden.content{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}.ui.slide.right.reveal>.visible.content{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}.ui.slide.right.reveal>.hidden.content{-webkit-transform:translateX(-100%)!important;transform:translateX(-100%)!important}.ui.slide.right.active.reveal>.visible.content,.ui.slide.right.reveal:hover>.visible.content{-webkit-transform:translateX(100%)!important;transform:translateX(100%)!important}.ui.slide.right.active.reveal>.hidden.content,.ui.slide.right.reveal:hover>.hidden.content{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}.ui.slide.up.reveal>.hidden.content{-webkit-transform:translateY(100%)!important;transform:translateY(100%)!important}.ui.slide.up.active.reveal>.visible.content,.ui.slide.up.reveal:hover>.visible.content{-webkit-transform:translateY(-100%)!important;transform:translateY(-100%)!important}.ui.slide.up.active.reveal>.hidden.content,.ui.slide.up.reveal:hover>.hidden.content{-webkit-transform:translateY(0)!important;transform:translateY(0)!important}.ui.slide.down.reveal>.hidden.content{-webkit-transform:translateY(-100%)!important;transform:translateY(-100%)!important}.ui.slide.down.active.reveal>.visible.content,.ui.slide.down.reveal:hover>.visible.content{-webkit-transform:translateY(100%)!important;transform:translateY(100%)!important}.ui.slide.down.active.reveal>.hidden.content,.ui.slide.down.reveal:hover>.hidden.content{-webkit-transform:translateY(0)!important;transform:translateY(0)!important}.ui.fade.reveal>.visible.content{opacity:1}.ui.fade.active.reveal>.visible.content,.ui.fade.reveal:hover>.visible.content{opacity:0}.ui.move.reveal{position:relative!important;overflow:hidden!important;white-space:nowrap}.ui.move.reveal>.content{display:block;float:left;margin:0;-webkit-transition:-webkit-transform .5s cubic-bezier(.175,.885,.32,1) .1s;transition:-webkit-transform .5s cubic-bezier(.175,.885,.32,1) .1s;transition:transform .5s cubic-bezier(.175,.885,.32,1) .1s;transition:transform .5s cubic-bezier(.175,.885,.32,1) .1s,-webkit-transform .5s cubic-bezier(.175,.885,.32,1) .1s}.ui.move.reveal>.visible.content{position:relative!important}.ui.move.reveal>.hidden.content{position:absolute!important;left:0!important;width:100%!important}.ui.move.active.reveal>.visible.content,.ui.move.reveal:hover>.visible.content{-webkit-transform:translateX(-100%)!important;transform:translateX(-100%)!important}.ui.move.right.active.reveal>.visible.content,.ui.move.right.reveal:hover>.visible.content{-webkit-transform:translateX(100%)!important;transform:translateX(100%)!important}.ui.move.up.active.reveal>.visible.content,.ui.move.up.reveal:hover>.visible.content{-webkit-transform:translateY(-100%)!important;transform:translateY(-100%)!important}.ui.move.down.active.reveal>.visible.content,.ui.move.down.reveal:hover>.visible.content{-webkit-transform:translateY(100%)!important;transform:translateY(100%)!important}.ui.rotate.reveal>.visible.content{-webkit-transition-duration:.5s;transition-duration:.5s;-webkit-transform:rotate(0);transform:rotate(0)}.ui.rotate.reveal>.visible.content,.ui.rotate.right.reveal>.visible.content{-webkit-transform-origin:bottom right;transform-origin:bottom right}.ui.rotate.active.reveal>.visible.content,.ui.rotate.reveal:hover>.visible.content,.ui.rotate.right.active.reveal>.visible.content,.ui.rotate.right.reveal:hover>.visible.content{-webkit-transform:rotate(110deg);transform:rotate(110deg)}.ui.rotate.left.reveal>.visible.content{-webkit-transform-origin:bottom left;transform-origin:bottom left}.ui.rotate.left.active.reveal>.visible.content,.ui.rotate.left.reveal:hover>.visible.content{-webkit-transform:rotate(-110deg);transform:rotate(-110deg)}.ui.disabled.reveal:hover>.visible.visible.content{position:static!important;display:block!important;opacity:1!important;top:0!important;left:0!important;right:auto!important;bottom:auto!important;-webkit-transform:none!important;transform:none!important}.ui.disabled.reveal:hover>.hidden.hidden.content{display:none!important}.ui.visible.reveal{overflow:visible}.ui.instant.reveal>.content{-webkit-transition-delay:0s!important;transition-delay:0s!important}.ui.reveal>.content{font-size:1rem!important} -------------------------------------------------------------------------------- /public/css/components/item.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Item 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.items>.item{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1em 0;width:100%;min-height:0;background:0 0;padding:0;border:none;border-radius:0;box-shadow:none;-webkit-transition:box-shadow .1s ease;transition:box-shadow .1s ease;z-index:''}.ui.items>.item a{cursor:pointer}.ui.items{margin:1.5em 0}.ui.items:first-child{margin-top:0!important}.ui.items:last-child{margin-bottom:0!important}.ui.items>.item:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item:first-child{margin-top:0}.ui.items>.item:last-child{margin-bottom:0}.ui.items>.item>.image{position:relative;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;display:block;float:none;margin:0;padding:0;max-height:'';-ms-flex-item-align:top;-ms-grid-row-align:top;align-self:top}.ui.items>.item>.image>img{display:block;width:100%;height:auto;border-radius:.125rem;border:none}.ui.items>.item>.image:only-child>img{border-radius:0}.ui.items>.item>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;background:0 0;margin:0;padding:0;box-shadow:none;font-size:1em;border:none;border-radius:0}.ui.items>.item>.content:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item>.image+.content{min-width:0;width:auto;display:block;margin-left:0;-ms-flex-item-align:top;-ms-grid-row-align:top;align-self:top;padding-left:1.5em}.ui.items>.item>.content>.header{display:inline-block;margin:-.21425em 0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;color:rgba(0,0,0,.85)}.ui.items>.item>.content>.header:not(.ui){font-size:1.28571429em}.ui.items>.item [class*="left floated"]{float:left}.ui.items>.item [class*="right floated"]{float:right}.ui.items>.item .content img{-ms-flex-item-align:middle;-ms-grid-row-align:middle;align-self:middle;width:''}.ui.items>.item .avatar img,.ui.items>.item img.avatar{width:'';height:'';border-radius:500rem}.ui.items>.item>.content>.description{margin-top:.6em;max-width:auto;font-size:1em;line-height:1.4285em;color:rgba(0,0,0,.87)}.ui.items>.item>.content p{margin:0 0 .5em}.ui.items>.item>.content p:last-child{margin-bottom:0}.ui.items>.item .meta{margin:.5em 0 .5em;font-size:1em;line-height:1em;color:rgba(0,0,0,.6)}.ui.items>.item .meta *{margin-right:.3em}.ui.items>.item .meta :last-child{margin-right:0}.ui.items>.item .meta [class*="right floated"]{margin-right:0;margin-left:.3em}.ui.items>.item>.content a:not(.ui){color:'';-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content a:not(.ui):hover{color:''}.ui.items>.item>.content>a.header{color:rgba(0,0,0,.85)}.ui.items>.item>.content>a.header:hover{color:#1e70bf}.ui.items>.item .meta>a:not(.ui){color:rgba(0,0,0,.4)}.ui.items>.item .meta>a:not(.ui):hover{color:rgba(0,0,0,.87)}.ui.items>.item>.content .favorite.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content .favorite.icon:hover{opacity:1;color:#ffb70a}.ui.items>.item>.content .active.favorite.icon{color:#ffe623}.ui.items>.item>.content .like.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content .like.icon:hover{opacity:1;color:#ff2733}.ui.items>.item>.content .active.like.icon{color:#ff2733}.ui.items>.item .extra{display:block;position:relative;background:0 0;margin:.5rem 0 0;width:100%;padding:0 0 0;top:0;left:0;color:rgba(0,0,0,.4);box-shadow:none;-webkit-transition:color .1s ease;transition:color .1s ease;border-top:none}.ui.items>.item .extra>*{margin:.25rem .5rem .25rem 0}.ui.items>.item .extra>[class*="right floated"]{margin:.25rem 0 .25rem .5rem}.ui.items>.item .extra:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item>.image:not(.ui){width:175px}@media only screen and (min-width:768px) and (max-width:991px){.ui.items>.item{margin:1em 0}.ui.items>.item>.image:not(.ui){width:150px}.ui.items>.item>.image+.content{display:block;padding:0 0 0 1em}}@media only screen and (max-width:767px){.ui.items:not(.unstackable)>.item{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:2em 0}.ui.items:not(.unstackable)>.item>.image{display:block;margin-left:auto;margin-right:auto}.ui.items:not(.unstackable)>.item>.image,.ui.items:not(.unstackable)>.item>.image>img{max-width:100%!important;width:auto!important;max-height:250px!important}.ui.items:not(.unstackable)>.item>.image+.content{display:block;padding:1.5em 0 0}}.ui.items>.item>.image+[class*="top aligned"].content{-ms-flex-item-align:start;align-self:flex-start}.ui.items>.item>.image+[class*="middle aligned"].content{-ms-flex-item-align:center;-ms-grid-row-align:center;align-self:center}.ui.items>.item>.image+[class*="bottom aligned"].content{-ms-flex-item-align:end;align-self:flex-end}.ui.relaxed.items>.item{margin:1.5em 0}.ui[class*="very relaxed"].items>.item{margin:2em 0}.ui.divided.items>.item{border-top:1px solid rgba(34,36,38,.15);margin:0;padding:1em 0}.ui.divided.items>.item:first-child{border-top:none;margin-top:0!important;padding-top:0!important}.ui.divided.items>.item:last-child{margin-bottom:0!important;padding-bottom:0!important}.ui.relaxed.divided.items>.item{margin:0;padding:1.5em 0}.ui[class*="very relaxed"].divided.items>.item{margin:0;padding:2em 0}.ui.items a.item:hover,.ui.link.items>.item:hover{cursor:pointer}.ui.items a.item:hover .content .header,.ui.link.items>.item:hover .content .header{color:#1e70bf}.ui.items>.item{font-size:1em}@media only screen and (max-width:767px){.ui.unstackable.items>.item>.image,.ui.unstackable.items>.item>.image>img{width:125px!important}} -------------------------------------------------------------------------------- /public/css/components/nag.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Nag 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | !function(e,o,t,n){"use strict";o="undefined"!=typeof o&&o.Math==Math?o:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.nag=function(t){var i,s=e(this),a=s.selector||"",r=(new Date).getTime(),l=[],c=arguments[0],g="string"==typeof c,u=[].slice.call(arguments,1);return s.each(function(){var s,d=e.isPlainObject(t)?e.extend(!0,{},e.fn.nag.settings,t):e.extend({},e.fn.nag.settings),m=(d.className,d.selector),f=d.error,p=d.namespace,h="."+p,b=p+"-module",v=e(this),y=(v.find(m.close),e(d.context?d.context:"body")),k=this,S=v.data(b);o.requestAnimationFrame||o.mozRequestAnimationFrame||o.webkitRequestAnimationFrame||o.msRequestAnimationFrame||function(e){setTimeout(e,0)};s={initialize:function(){s.verbose("Initializing element"),v.on("click"+h,m.close,s.dismiss).data(b,s),d.detachable&&v.parent()[0]!==y[0]&&v.detach().prependTo(y),d.displayTime>0&&setTimeout(s.hide,d.displayTime),s.show()},destroy:function(){s.verbose("Destroying instance"),v.removeData(b).off(h)},show:function(){s.should.show()&&!v.is(":visible")&&(s.debug("Showing nag",d.animation.show),"fade"==d.animation.show?v.fadeIn(d.duration,d.easing):v.slideDown(d.duration,d.easing))},hide:function(){s.debug("Showing nag",d.animation.hide),"fade"==d.animation.show?v.fadeIn(d.duration,d.easing):v.slideUp(d.duration,d.easing)},onHide:function(){s.debug("Removing nag",d.animation.hide),v.remove(),d.onHide&&d.onHide()},dismiss:function(e){d.storageMethod&&s.storage.set(d.key,d.value),s.hide(),e.stopImmediatePropagation(),e.preventDefault()},should:{show:function(){return d.persist?(s.debug("Persistent nag is set, can show nag"),!0):s.storage.get(d.key)!=d.value.toString()?(s.debug("Stored value is not set, can show nag",s.storage.get(d.key)),!0):(s.debug("Stored value is set, cannot show nag",s.storage.get(d.key)),!1)}},get:{storageOptions:function(){var e={};return d.expires&&(e.expires=d.expires),d.domain&&(e.domain=d.domain),d.path&&(e.path=d.path),e}},clear:function(){s.storage.remove(d.key)},storage:{set:function(t,i){var a=s.get.storageOptions();if("localstorage"==d.storageMethod&&o.localStorage!==n)o.localStorage.setItem(t,i),s.debug("Value stored using local storage",t,i);else if("sessionstorage"==d.storageMethod&&o.sessionStorage!==n)o.sessionStorage.setItem(t,i),s.debug("Value stored using session storage",t,i);else{if(e.cookie===n)return void s.error(f.noCookieStorage);e.cookie(t,i,a),s.debug("Value stored using cookie",t,i,a)}},get:function(t,i){var a;return"localstorage"==d.storageMethod&&o.localStorage!==n?a=o.localStorage.getItem(t):"sessionstorage"==d.storageMethod&&o.sessionStorage!==n?a=o.sessionStorage.getItem(t):e.cookie!==n?a=e.cookie(t):s.error(f.noCookieStorage),"undefined"!=a&&"null"!=a&&a!==n&&null!==a||(a=n),a},remove:function(t){var i=s.get.storageOptions();"localstorage"==d.storageMethod&&o.localStorage!==n?o.localStorage.removeItem(t):"sessionstorage"==d.storageMethod&&o.sessionStorage!==n?o.sessionStorage.removeItem(t):e.cookie!==n?e.removeCookie(t,i):s.error(f.noStorage)}},setting:function(o,t){if(s.debug("Changing setting",o,t),e.isPlainObject(o))e.extend(!0,d,o);else{if(t===n)return d[o];e.isPlainObject(d[o])?e.extend(!0,d[o],t):d[o]=t}},internal:function(o,t){if(e.isPlainObject(o))e.extend(!0,s,o);else{if(t===n)return s[o];s[o]=t}},debug:function(){!d.silent&&d.debug&&(d.performance?s.performance.log(arguments):(s.debug=Function.prototype.bind.call(console.info,console,d.name+":"),s.debug.apply(console,arguments)))},verbose:function(){!d.silent&&d.verbose&&d.debug&&(d.performance?s.performance.log(arguments):(s.verbose=Function.prototype.bind.call(console.info,console,d.name+":"),s.verbose.apply(console,arguments)))},error:function(){d.silent||(s.error=Function.prototype.bind.call(console.error,console,d.name+":"),s.error.apply(console,arguments))},performance:{log:function(e){var o,t,n;d.performance&&(o=(new Date).getTime(),n=r||o,t=o-n,r=o,l.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:k,"Execution Time":t})),clearTimeout(s.performance.timer),s.performance.timer=setTimeout(s.performance.display,500)},display:function(){var o=d.name+":",t=0;r=!1,clearTimeout(s.performance.timer),e.each(l,function(e,o){t+=o["Execution Time"]}),o+=" "+t+"ms",a&&(o+=" '"+a+"'"),(console.group!==n||console.table!==n)&&l.length>0&&(console.groupCollapsed(o),console.table?console.table(l):e.each(l,function(e,o){console.log(o.Name+": "+o["Execution Time"]+"ms")}),console.groupEnd()),l=[]}},invoke:function(o,t,a){var r,l,c,g=S;return t=t||u,a=k||a,"string"==typeof o&&g!==n&&(o=o.split(/[\. ]/),r=o.length-1,e.each(o,function(t,i){var a=t!=r?i+o[t+1].charAt(0).toUpperCase()+o[t+1].slice(1):o;if(e.isPlainObject(g[a])&&t!=r)g=g[a];else{if(g[a]!==n)return l=g[a],!1;if(!e.isPlainObject(g[i])||t==r)return g[i]!==n?(l=g[i],!1):(s.error(f.method,o),!1);g=g[i]}})),e.isFunction(l)?c=l.apply(a,t):l!==n&&(c=l),e.isArray(i)?i.push(c):i!==n?i=[i,c]:c!==n&&(i=c),l}},g?(S===n&&s.initialize(),s.invoke(c)):(S!==n&&S.invoke("destroy"),s.initialize())}),i!==n?i:this},e.fn.nag.settings={name:"Nag",silent:!1,debug:!1,verbose:!1,performance:!0,namespace:"Nag",persist:!1,displayTime:0,animation:{show:"slide",hide:"slide"},context:!1,detachable:!1,expires:30,domain:!1,path:"/",storageMethod:"cookie",key:"nag",value:"dismiss",error:{noCookieStorage:"$.cookie is not included. A storage solution is required.",noStorage:"Neither $.cookie or store is defined. A storage solution is required for storing state",method:"The method you called is not defined."},className:{bottom:"bottom",fixed:"fixed"},selector:{close:".close.icon"},speed:500,easing:"easeOutQuad",onHide:function(){}},e.extend(e.easing,{easeOutQuad:function(e,o,t,n,i){return-n*(o/=i)*(o-2)+t}})}(jQuery,window,document); -------------------------------------------------------------------------------- /routes/posts.js: -------------------------------------------------------------------------------- 1 | //处理所有以/post/开始的请求 2 | 3 | const koaRouter = require('koa-router'); 4 | const checkLogin = require('../middlewares/check').checkLogin; 5 | const postModel = require('../models/posts'); 6 | const commentModel = require('../models/comments'); 7 | 8 | //设定路由前缀 9 | const router = new koaRouter({ 10 | prefix: '/posts' 11 | }); 12 | 13 | //GET /posts 所有用户或者特定用户的文章页 14 | router.get('/', async (ctx,next) => { 15 | console.log('所有用户或者特定用户的文章页'); 16 | let author = ctx.query.author; 17 | let posts = await postModel.getPosts(author); 18 | console.log(posts); 19 | try { 20 | await ctx.render('posts', {title: '首页',posts: posts}); 21 | }catch(e){ 22 | console.log(e.message); 23 | } 24 | }); 25 | 26 | //GET /posts/create 发表文章页 27 | router.get('/create',checkLogin,async (ctx,next) => { 28 | console.log('发表文章页'); 29 | await ctx.render('create',{title: '写文章'}); 30 | }); 31 | 32 | //POST /posts 发表一篇文章 33 | router.post('/',checkLogin,async (ctx,next) => { 34 | console.log('发表一篇文章'); 35 | let author = ctx.session.user._id; 36 | let title = ctx.request.body.title; 37 | let content = ctx.request.body.content; 38 | 39 | //校检参数 40 | try{ 41 | console.log('校检参数'); 42 | if(!title.length){ 43 | throw new Error('请填写标题'); 44 | } 45 | if(!content.length){ 46 | throw new Error('请填写内容'); 47 | } 48 | }catch(e){ 49 | console.log('校检参数出错'); 50 | ctx.flash('error',e.message); 51 | return ctx.redirect('back'); 52 | } 53 | 54 | let post = { 55 | author : author, 56 | title : title, 57 | content : content, 58 | pv: 0 //点击量 59 | }; 60 | 61 | try{ 62 | console.log('post data to mongodb'); 63 | let result = await postModel.create(post); 64 | //result是插入mongodb后的值,包含_id; 65 | post = result.ops[0]; 66 | ctx.flash('success','发表成功'); 67 | ctx.redirect(`/posts/${post._id}`); 68 | }catch(e){ 69 | //handler error 70 | console.log('post error : ' + e.message); 71 | } 72 | 73 | }); 74 | 75 | //GET /posts/:postId 单独一篇文章页 76 | router.get('/:postId',async (ctx,next) => { 77 | console.log('单独一篇文章页'); 78 | let postId = ctx.params.postId; 79 | 80 | try{ 81 | let result = await Promise.all([postModel.getPostById(postId),postModel.incPv(postId),commentModel.getComments(postId)]); 82 | let post = result[0]; 83 | let comments = result[2]; 84 | console.log(comments); 85 | if(!post){ 86 | throw new Error('该文章不存在'); 87 | } 88 | await ctx.render('post',{item : post , comments : comments , title: post.title}); 89 | }catch(e){ 90 | console.log(e.message); 91 | } 92 | }); 93 | 94 | //GET /posts/:postId/edit 更新文章页 95 | router.get('/:postId/edit',checkLogin,async (ctx,next) => { 96 | console.log('更新文章页'); 97 | let postId = ctx.params.postId; 98 | let author = ctx.session.user._id; 99 | 100 | try{ 101 | let post = await postModel.getRawPostById(postId,author); 102 | if(!post){ 103 | throw new Error('该文章不存在'); 104 | } 105 | if(author.toString() !== post.author._id.toString()){ 106 | throw new Error('权限不足'); 107 | } 108 | await ctx.render('edit',{ 109 | post: post, 110 | title:'编辑文章' 111 | }); 112 | }catch(e){ 113 | console.log(e.message); 114 | } 115 | 116 | }) ; 117 | 118 | //POST /posts/:postId/edit 更新一篇文章 119 | router.post('/:postId/edit',checkLogin,async (ctx,next) => { 120 | console.log('更新一篇文章'); 121 | let postId = ctx.params.postId; 122 | let author = ctx.session.user._id; 123 | let title = ctx.request.body.title; 124 | let content = ctx.request.body.content; 125 | try{ 126 | await postModel.updatePostById(postId,author,{title: title,content: content}); 127 | ctx.flash('success','更新成功'); 128 | ctx.redirect(`/posts/${postId}`); 129 | }catch(e){ 130 | console.log(e.message); 131 | } 132 | }); 133 | 134 | //DEL /posts/:postId/remove 删除一篇文章 135 | router.get('/:postId/remove',checkLogin,async (ctx,next) => { 136 | console.log('删除一篇文章'); 137 | let postId = ctx.params.postId; 138 | let author = ctx.session.user._id; 139 | 140 | try{ 141 | await postModel.delPostById(postId,author); 142 | ctx.flash('success','删除文章成功'); 143 | //转到主页 144 | ctx.redirect('/posts'); 145 | }catch(e){ 146 | console.log(e.message); 147 | } 148 | }); 149 | 150 | //POST /posts/:postId/comment 创建一条留言 151 | router.post('/:postId/comment',checkLogin,async (ctx,next) => { 152 | console.log('创建一条留言'); 153 | let author = ctx.session.user._id; 154 | let postId = ctx.params.postId; 155 | let content = ctx.request.body.content; 156 | let comment = { 157 | author: author, 158 | postId: postId, 159 | content: content 160 | }; 161 | try{ 162 | let result = await commentModel.create(comment); 163 | ctx.flash('success','留言成功'); 164 | ctx.redirect('back'); 165 | }catch(e){ 166 | console.log(e.message); 167 | } 168 | }); 169 | 170 | //DELETE /posts/:postId/comment/:commentId/remove 删除一条留言 171 | router.get('/:postId/comment/:commentId/remove',checkLogin,async (ctx,next) => { 172 | console.log('删除一条留言'); 173 | let commentId = ctx.params.commentId; 174 | let author = ctx.session.user._id; 175 | try{ 176 | await commentModel.delCommentById(commentId,author); 177 | //删除成功后跳转到上一页 178 | ctx.redirect('back'); 179 | }catch(e){ 180 | console.log(e.message); 181 | } 182 | }); 183 | 184 | //输出 185 | module.exports = router; 186 | -------------------------------------------------------------------------------- /public/css/components/divider.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Divider 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.divider{margin:1rem 0;line-height:1;height:0;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:rgba(0,0,0,.85);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ui.divider:not(.vertical):not(.horizontal){border-top:1px solid rgba(34,36,38,.15);border-bottom:1px solid rgba(255,255,255,.1)}.ui.grid>.column+.divider,.ui.grid>.row>.column+.divider{left:auto}.ui.horizontal.divider{display:table;white-space:nowrap;height:auto;margin:'';line-height:1;text-align:center}.ui.horizontal.divider:after,.ui.horizontal.divider:before{content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.horizontal.divider:before{background-position:right 1em top 50%}.ui.horizontal.divider:after{background-position:left 1em top 50%}.ui.vertical.divider{position:absolute;z-index:2;top:50%;left:50%;margin:0;padding:0;width:auto;height:50%;line-height:0;text-align:center;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ui.vertical.divider:after,.ui.vertical.divider:before{position:absolute;left:50%;content:'';z-index:3;border-left:1px solid rgba(34,36,38,.15);border-right:1px solid rgba(255,255,255,.1);width:0;height:calc(100% - 1rem)}.ui.vertical.divider:before{top:-100%}.ui.vertical.divider:after{top:auto;bottom:0}@media only screen and (max-width:767px){.ui.grid .stackable.row .ui.vertical.divider,.ui.stackable.grid .ui.vertical.divider{display:table;white-space:nowrap;height:auto;margin:'';overflow:hidden;line-height:1;text-align:center;position:static;top:0;left:0;-webkit-transform:none;transform:none}.ui.grid .stackable.row .ui.vertical.divider:after,.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:before{position:static;left:0;border-left:none;border-right:none;content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:before{background-position:right 1em top 50%}.ui.grid .stackable.row .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:after{background-position:left 1em top 50%}}.ui.divider>.icon{margin:0;font-size:1rem;height:1em;vertical-align:middle}.ui.hidden.divider{border-color:transparent!important}.ui.hidden.divider:after,.ui.hidden.divider:before{display:none}.ui.divider.inverted,.ui.horizontal.inverted.divider,.ui.vertical.inverted.divider{color:#fff}.ui.divider.inverted,.ui.divider.inverted:after,.ui.divider.inverted:before{border-top-color:rgba(34,36,38,.15)!important;border-left-color:rgba(34,36,38,.15)!important;border-bottom-color:rgba(255,255,255,.15)!important;border-right-color:rgba(255,255,255,.15)!important}.ui.fitted.divider{margin:0}.ui.clearing.divider{clear:both}.ui.section.divider{margin-top:2rem;margin-bottom:2rem}.ui.divider{font-size:1rem}.ui.horizontal.divider:after,.ui.horizontal.divider:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC)}@media only screen and (max-width:767px){.ui.grid .stackable.row .ui.vertical.divider:after,.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC)}} -------------------------------------------------------------------------------- /public/css/components/search.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Search 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.search{position:relative}.ui.search>.prompt{margin:0;outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);text-shadow:none;font-style:normal;font-weight:400;line-height:1.21428571em;padding:.67857143em 1em;font-size:1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);box-shadow:0 0 0 0 transparent inset;-webkit-transition:background-color .1s ease,color .1s ease,box-shadow .1s ease,border-color .1s ease;transition:background-color .1s ease,color .1s ease,box-shadow .1s ease,border-color .1s ease}.ui.search .prompt{border-radius:500rem}.ui.search .prompt~.search.icon{cursor:pointer}.ui.search>.results{display:none;position:absolute;top:100%;left:0;-webkit-transform-origin:center top;transform-origin:center top;white-space:normal;background:#fff;margin-top:.5em;width:18em;border-radius:.28571429rem;box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);border:1px solid #d4d4d5;z-index:998}.ui.search>.results>:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.search>.results>:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.search>.results .result{cursor:pointer;display:block;overflow:hidden;font-size:1em;padding:.85714286em 1.14285714em;color:rgba(0,0,0,.87);line-height:1.33;border-bottom:1px solid rgba(34,36,38,.1)}.ui.search>.results .result:last-child{border-bottom:none!important}.ui.search>.results .result .image{float:right;overflow:hidden;background:0 0;width:5em;height:3em;border-radius:.25em}.ui.search>.results .result .image img{display:block;width:auto;height:100%}.ui.search>.results .result .image+.content{margin:0 6em 0 0}.ui.search>.results .result .title{margin:-.14285714em 0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;font-size:1em;color:rgba(0,0,0,.85)}.ui.search>.results .result .description{margin-top:0;font-size:.92857143em;color:rgba(0,0,0,.4)}.ui.search>.results .result .price{float:right;color:#21ba45}.ui.search>.results>.message{padding:1em 1em}.ui.search>.results>.message .header{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1rem;font-weight:700;color:rgba(0,0,0,.87)}.ui.search>.results>.message .description{margin-top:.25rem;font-size:1em;color:rgba(0,0,0,.87)}.ui.search>.results>.action{display:block;border-top:none;background:#f3f4f5;padding:.92857143em 1em;color:rgba(0,0,0,.87);font-weight:700;text-align:center}.ui.search>.prompt:focus{border-color:rgba(34,36,38,.35);background:#fff;color:rgba(0,0,0,.95)}.ui.loading.search .input>i.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.search .input>i.icon:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;-webkit-animation:button-spin .6s linear;animation:button-spin .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#767676 transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent}.ui.category.search>.results .category .result:hover,.ui.search>.results .result:hover{background:#f9fafb}.ui.search .action:hover{background:#e0e0e0}.ui.category.search>.results .category.active{background:#f3f4f5}.ui.category.search>.results .category.active>.name{color:rgba(0,0,0,.87)}.ui.category.search>.results .category .result.active,.ui.search>.results .result.active{position:relative;border-left-color:rgba(34,36,38,.1);background:#f3f4f5;box-shadow:none}.ui.search>.results .result.active .title{color:rgba(0,0,0,.85)}.ui.search>.results .result.active .description{color:rgba(0,0,0,.85)}.ui.search.selection .prompt{border-radius:.28571429rem}.ui.search.selection>.icon.input>.remove.icon{pointer-events:none;position:absolute;left:auto;opacity:0;color:'';top:0;right:0;-webkit-transition:color .1s ease,opacity .1s ease;transition:color .1s ease,opacity .1s ease}.ui.search.selection>.icon.input>.active.remove.icon{cursor:pointer;opacity:.8;pointer-events:auto}.ui.search.selection>.icon.input:not([class*="left icon"])>.icon~.remove.icon{right:1.85714em}.ui.search.selection>.icon.input>.remove.icon:hover{opacity:1;color:#db2828}.ui.category.search .results{width:28em}.ui.category.search>.results .category{background:#f3f4f5;box-shadow:none;border-bottom:1px solid rgba(34,36,38,.1);-webkit-transition:background .1s ease,border-color .1s ease;transition:background .1s ease,border-color .1s ease}.ui.category.search>.results .category:last-child{border-bottom:none}.ui.category.search>.results .category:first-child .name+.result{border-radius:0 .28571429rem 0 0}.ui.category.search>.results .category:last-child .result:last-child{border-radius:0 0 .28571429rem 0}.ui.category.search>.results .category .result{background:#fff;margin-left:100px;border-left:1px solid rgba(34,36,38,.15);border-bottom:1px solid rgba(34,36,38,.1);-webkit-transition:background .1s ease,border-color .1s ease;transition:background .1s ease,border-color .1s ease;padding:.85714286em 1.14285714em}.ui.category.search>.results .category:last-child .result:last-child{border-bottom:none}.ui.category.search>.results .category>.name{width:100px;background:0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;float:1em;float:left;padding:.4em 1em;font-weight:700;color:rgba(0,0,0,.4)}.ui[class*="left aligned"].search>.results{right:auto;left:0}.ui[class*="right aligned"].search>.results{right:0;left:auto}.ui.fluid.search .results{width:100%}.ui.mini.search{font-size:.78571429em}.ui.small.search{font-size:.92857143em}.ui.search{font-size:1em}.ui.large.search{font-size:1.14285714em}.ui.big.search{font-size:1.28571429em}.ui.huge.search{font-size:1.42857143em}.ui.massive.search{font-size:1.71428571em} -------------------------------------------------------------------------------- /public/css/components/site.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Site 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | !function(e,n,o,i){e.site=e.fn.site=function(t){var s,r,a=(new Date).getTime(),c=[],l=arguments[0],u="string"==typeof l,d=[].slice.call(arguments,1),f=e.isPlainObject(t)?e.extend(!0,{},e.site.settings,t):e.extend({},e.site.settings),m=f.namespace,g=f.error,b="module-"+m,p=e(o),v=p,h=this,y=v.data(b);return s={initialize:function(){s.instantiate()},instantiate:function(){s.verbose("Storing instance of site",s),y=s,v.data(b,s)},normalize:function(){s.fix.console(),s.fix.requestAnimationFrame()},fix:{console:function(){s.debug("Normalizing window.console"),console!==i&&console.log!==i||(s.verbose("Console not available, normalizing events"),s.disable.console()),"undefined"!=typeof console.group&&"undefined"!=typeof console.groupEnd&&"undefined"!=typeof console.groupCollapsed||(s.verbose("Console group not available, normalizing events"),n.console.group=function(){},n.console.groupEnd=function(){},n.console.groupCollapsed=function(){}),"undefined"==typeof console.markTimeline&&(s.verbose("Mark timeline not available, normalizing events"),n.console.markTimeline=function(){})},consoleClear:function(){s.debug("Disabling programmatic console clearing"),n.console.clear=function(){}},requestAnimationFrame:function(){s.debug("Normalizing requestAnimationFrame"),n.requestAnimationFrame===i&&(s.debug("RequestAnimationFrame not available, normalizing event"),n.requestAnimationFrame=n.requestAnimationFrame||n.mozRequestAnimationFrame||n.webkitRequestAnimationFrame||n.msRequestAnimationFrame||function(e){setTimeout(e,0)})}},moduleExists:function(n){return e.fn[n]!==i&&e.fn[n].settings!==i},enabled:{modules:function(n){var o=[];return n=n||f.modules,e.each(n,function(e,n){s.moduleExists(n)&&o.push(n)}),o}},disabled:{modules:function(n){var o=[];return n=n||f.modules,e.each(n,function(e,n){s.moduleExists(n)||o.push(n)}),o}},change:{setting:function(n,o,t,r){t="string"==typeof t?"all"===t?f.modules:[t]:t||f.modules,r=r===i||r,e.each(t,function(i,t){var a,c=!s.moduleExists(t)||(e.fn[t].settings.namespace||!1);s.moduleExists(t)&&(s.verbose("Changing default setting",n,o,t),e.fn[t].settings[n]=o,r&&c&&(a=e(":data(module-"+c+")"),a.length>0&&(s.verbose("Modifying existing settings",a),a[t]("setting",n,o))))})},settings:function(n,o,t){o="string"==typeof o?[o]:o||f.modules,t=t===i||t,e.each(o,function(o,i){var r;s.moduleExists(i)&&(s.verbose("Changing default setting",n,i),e.extend(!0,e.fn[i].settings,n),t&&m&&(r=e(":data(module-"+m+")"),r.length>0&&(s.verbose("Modifying existing settings",r),r[i]("setting",n))))})}},enable:{console:function(){s.console(!0)},debug:function(e,n){e=e||f.modules,s.debug("Enabling debug for modules",e),s.change.setting("debug",!0,e,n)},verbose:function(e,n){e=e||f.modules,s.debug("Enabling verbose debug for modules",e),s.change.setting("verbose",!0,e,n)}},disable:{console:function(){s.console(!1)},debug:function(e,n){e=e||f.modules,s.debug("Disabling debug for modules",e),s.change.setting("debug",!1,e,n)},verbose:function(e,n){e=e||f.modules,s.debug("Disabling verbose debug for modules",e),s.change.setting("verbose",!1,e,n)}},console:function(e){if(e){if(y.cache.console===i)return void s.error(g.console);s.debug("Restoring console function"),n.console=y.cache.console}else s.debug("Disabling console function"),y.cache.console=n.console,n.console={clear:function(){},error:function(){},group:function(){},groupCollapsed:function(){},groupEnd:function(){},info:function(){},log:function(){},markTimeline:function(){},warn:function(){}}},destroy:function(){s.verbose("Destroying previous site for",v),v.removeData(b)},cache:{},setting:function(n,o){if(e.isPlainObject(n))e.extend(!0,f,n);else{if(o===i)return f[n];f[n]=o}},internal:function(n,o){if(e.isPlainObject(n))e.extend(!0,s,n);else{if(o===i)return s[n];s[n]=o}},debug:function(){f.debug&&(f.performance?s.performance.log(arguments):(s.debug=Function.prototype.bind.call(console.info,console,f.name+":"),s.debug.apply(console,arguments)))},verbose:function(){f.verbose&&f.debug&&(f.performance?s.performance.log(arguments):(s.verbose=Function.prototype.bind.call(console.info,console,f.name+":"),s.verbose.apply(console,arguments)))},error:function(){s.error=Function.prototype.bind.call(console.error,console,f.name+":"),s.error.apply(console,arguments)},performance:{log:function(e){var n,o,i;f.performance&&(n=(new Date).getTime(),i=a||n,o=n-i,a=n,c.push({Element:h,Name:e[0],Arguments:[].slice.call(e,1)||"","Execution Time":o})),clearTimeout(s.performance.timer),s.performance.timer=setTimeout(s.performance.display,500)},display:function(){var n=f.name+":",o=0;a=!1,clearTimeout(s.performance.timer),e.each(c,function(e,n){o+=n["Execution Time"]}),n+=" "+o+"ms",(console.group!==i||console.table!==i)&&c.length>0&&(console.groupCollapsed(n),console.table?console.table(c):e.each(c,function(e,n){console.log(n.Name+": "+n["Execution Time"]+"ms")}),console.groupEnd()),c=[]}},invoke:function(n,o,t){var a,c,l,u=y;return o=o||d,t=h||t,"string"==typeof n&&u!==i&&(n=n.split(/[\. ]/),a=n.length-1,e.each(n,function(o,t){var r=o!=a?t+n[o+1].charAt(0).toUpperCase()+n[o+1].slice(1):n;if(e.isPlainObject(u[r])&&o!=a)u=u[r];else{if(u[r]!==i)return c=u[r],!1;if(!e.isPlainObject(u[t])||o==a)return u[t]!==i?(c=u[t],!1):(s.error(g.method,n),!1);u=u[t]}})),e.isFunction(c)?l=c.apply(t,o):c!==i&&(l=c),e.isArray(r)?r.push(l):r!==i?r=[r,l]:l!==i&&(r=l),c}},u?(y===i&&s.initialize(),s.invoke(l)):(y!==i&&s.destroy(),s.initialize()),r!==i?r:this},e.site.settings={name:"Site",namespace:"site",error:{console:"Console cannot be restored, most likely it was overwritten outside of module",method:"The method you called is not defined."},debug:!1,verbose:!1,performance:!0,modules:["accordion","api","checkbox","dimmer","dropdown","embed","form","modal","nag","popup","rating","shape","sidebar","state","sticky","tab","transition","visit","visibility"],siteNamespace:"site",namespaceStub:{cache:{},config:{},sections:{},section:{},utilities:{}}},e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(n){return function(o){return!!e.data(o,n)}}):function(n,o,i){return!!e.data(n,i[3])}})}(jQuery,window,document); -------------------------------------------------------------------------------- /public/css/components/comment.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Comment 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Standard 14 | *******************************/ 15 | 16 | 17 | /*-------------- 18 | Comments 19 | ---------------*/ 20 | 21 | .ui.comments { 22 | margin: 1.5em 0em; 23 | max-width: 650px; 24 | } 25 | .ui.comments:first-child { 26 | margin-top: 0em; 27 | } 28 | .ui.comments:last-child { 29 | margin-bottom: 0em; 30 | } 31 | 32 | /*-------------- 33 | Comment 34 | ---------------*/ 35 | 36 | .ui.comments .comment { 37 | position: relative; 38 | background: none; 39 | margin: 0.5em 0em 0em; 40 | padding: 0.5em 0em 0em; 41 | border: none; 42 | border-top: none; 43 | line-height: 1.2; 44 | } 45 | .ui.comments .comment:first-child { 46 | margin-top: 0em; 47 | padding-top: 0em; 48 | } 49 | 50 | /*-------------------- 51 | Nested Comments 52 | ---------------------*/ 53 | 54 | .ui.comments .comment .comments { 55 | margin: 0em 0em 0.5em 0.5em; 56 | padding: 1em 0em 1em 1em; 57 | } 58 | .ui.comments .comment .comments:before { 59 | position: absolute; 60 | top: 0px; 61 | left: 0px; 62 | } 63 | .ui.comments .comment .comments .comment { 64 | border: none; 65 | border-top: none; 66 | background: none; 67 | } 68 | 69 | /*-------------- 70 | Avatar 71 | ---------------*/ 72 | 73 | .ui.comments .comment .avatar { 74 | display: block; 75 | width: 2.5em; 76 | height: auto; 77 | float: left; 78 | margin: 0.2em 0em 0em; 79 | } 80 | .ui.comments .comment img.avatar, 81 | .ui.comments .comment .avatar img { 82 | display: block; 83 | margin: 0em auto; 84 | width: 100%; 85 | height: 100%; 86 | border-radius: 0.25rem; 87 | } 88 | 89 | /*-------------- 90 | Content 91 | ---------------*/ 92 | 93 | .ui.comments .comment > .content { 94 | display: block; 95 | } 96 | 97 | /* If there is an avatar move content over */ 98 | .ui.comments .comment > .avatar ~ .content { 99 | margin-left: 3.5em; 100 | } 101 | 102 | /*-------------- 103 | Author 104 | ---------------*/ 105 | 106 | .ui.comments .comment .author { 107 | font-size: 1em; 108 | color: rgba(0, 0, 0, 0.87); 109 | font-weight: bold; 110 | } 111 | .ui.comments .comment a.author { 112 | cursor: pointer; 113 | } 114 | .ui.comments .comment a.author:hover { 115 | color: #1e70bf; 116 | } 117 | 118 | /*-------------- 119 | Metadata 120 | ---------------*/ 121 | 122 | .ui.comments .comment .metadata { 123 | display: inline-block; 124 | margin-left: 0.5em; 125 | color: rgba(0, 0, 0, 0.4); 126 | font-size: 0.875em; 127 | } 128 | .ui.comments .comment .metadata > * { 129 | display: inline-block; 130 | margin: 0em 0.5em 0em 0em; 131 | } 132 | .ui.comments .comment .metadata > :last-child { 133 | margin-right: 0em; 134 | } 135 | 136 | /*-------------------- 137 | Comment Text 138 | ---------------------*/ 139 | 140 | .ui.comments .comment .text { 141 | margin: 0.25em 0em 0.5em; 142 | font-size: 1em; 143 | word-wrap: break-word; 144 | color: rgba(0, 0, 0, 0.87); 145 | line-height: 1.3; 146 | } 147 | 148 | /*-------------------- 149 | User Actions 150 | ---------------------*/ 151 | 152 | .ui.comments .comment .actions { 153 | font-size: 0.875em; 154 | } 155 | .ui.comments .comment .actions a { 156 | cursor: pointer; 157 | display: inline-block; 158 | margin: 0em 0.75em 0em 0em; 159 | color: rgba(0, 0, 0, 0.4); 160 | } 161 | .ui.comments .comment .actions a:last-child { 162 | margin-right: 0em; 163 | } 164 | .ui.comments .comment .actions a.active, 165 | .ui.comments .comment .actions a:hover { 166 | color: rgba(0, 0, 0, 0.8); 167 | } 168 | 169 | /*-------------------- 170 | Reply Form 171 | ---------------------*/ 172 | 173 | .ui.comments > .reply.form { 174 | margin-top: 1em; 175 | } 176 | .ui.comments .comment .reply.form { 177 | width: 100%; 178 | margin-top: 1em; 179 | } 180 | .ui.comments .reply.form textarea { 181 | font-size: 1em; 182 | height: 12em; 183 | } 184 | 185 | 186 | /******************************* 187 | State 188 | *******************************/ 189 | 190 | .ui.collapsed.comments, 191 | .ui.comments .collapsed.comments, 192 | .ui.comments .collapsed.comment { 193 | display: none; 194 | } 195 | 196 | 197 | /******************************* 198 | Variations 199 | *******************************/ 200 | 201 | 202 | /*-------------------- 203 | Threaded 204 | ---------------------*/ 205 | 206 | .ui.threaded.comments .comment .comments { 207 | margin: -1.5em 0 -1em 1.25em; 208 | padding: 3em 0em 2em 2.25em; 209 | box-shadow: -1px 0px 0px rgba(34, 36, 38, 0.15); 210 | } 211 | 212 | /*-------------------- 213 | Minimal 214 | ---------------------*/ 215 | 216 | .ui.minimal.comments .comment .actions { 217 | opacity: 0; 218 | position: absolute; 219 | top: 0px; 220 | right: 0px; 221 | left: auto; 222 | -webkit-transition: opacity 0.2s ease; 223 | transition: opacity 0.2s ease; 224 | -webkit-transition-delay: 0.1s; 225 | transition-delay: 0.1s; 226 | } 227 | .ui.minimal.comments .comment > .content:hover > .actions { 228 | opacity: 1; 229 | } 230 | 231 | /*------------------- 232 | Sizes 233 | --------------------*/ 234 | 235 | .ui.mini.comments { 236 | font-size: 0.78571429rem; 237 | } 238 | .ui.tiny.comments { 239 | font-size: 0.85714286rem; 240 | } 241 | .ui.small.comments { 242 | font-size: 0.92857143rem; 243 | } 244 | .ui.comments { 245 | font-size: 1rem; 246 | } 247 | .ui.large.comments { 248 | font-size: 1.14285714rem; 249 | } 250 | .ui.big.comments { 251 | font-size: 1.28571429rem; 252 | } 253 | .ui.huge.comments { 254 | font-size: 1.42857143rem; 255 | } 256 | .ui.massive.comments { 257 | font-size: 1.71428571rem; 258 | } 259 | 260 | 261 | /******************************* 262 | Theme Overrides 263 | *******************************/ 264 | 265 | 266 | 267 | /******************************* 268 | User Variable Overrides 269 | *******************************/ 270 | 271 | -------------------------------------------------------------------------------- /public/css/components/modal.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Modal 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.modal{display:none;position:fixed;z-index:1001;top:50%;left:50%;text-align:left;background:#fff;border:none;box-shadow:1px 3px 3px 0 rgba(0,0,0,.2),1px 3px 15px 2px rgba(0,0,0,.2);-webkit-transform-origin:50% 25%;transform-origin:50% 25%;border-radius:.28571429rem;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;will-change:top,left,margin,transform,opacity}.ui.modal>.icon:first-child+*,.ui.modal>:first-child:not(.icon){border-top-left-radius:.28571429rem;border-top-right-radius:.28571429rem}.ui.modal>:last-child{border-bottom-left-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.modal>.close{cursor:pointer;position:absolute;top:-2.5rem;right:-2.5rem;z-index:1;opacity:.8;font-size:1.25em;color:#fff;width:2.25rem;height:2.25rem;padding:.625rem 0 0 0}.ui.modal>.close:hover{opacity:1}.ui.modal>.header{display:block;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;background:#fff;margin:0;padding:1.25rem 1.5rem;box-shadow:none;color:rgba(0,0,0,.85);border-bottom:1px solid rgba(34,36,38,.15)}.ui.modal>.header:not(.ui){font-size:1.42857143rem;line-height:1.28571429em;font-weight:700}.ui.modal>.content{display:block;width:100%;font-size:1em;line-height:1.4;padding:1.5rem;background:#fff}.ui.modal>.image.content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.ui.modal>.content>.image{display:block;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:'';-ms-flex-item-align:top;-ms-grid-row-align:top;align-self:top}.ui.modal>[class*="top aligned"]{-ms-flex-item-align:top;-ms-grid-row-align:top;align-self:top}.ui.modal>[class*="middle aligned"]{-ms-flex-item-align:middle;-ms-grid-row-align:middle;align-self:middle}.ui.modal>[class*=stretched]{-ms-flex-item-align:stretch;-ms-grid-row-align:stretch;align-self:stretch}.ui.modal>.content>.description{display:block;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;min-width:0;-ms-flex-item-align:top;-ms-grid-row-align:top;align-self:top}.ui.modal>.content>.icon+.description,.ui.modal>.content>.image+.description{-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;min-width:'';width:auto;padding-left:2em}.ui.modal>.content>.image>i.icon{margin:0;opacity:1;width:auto;line-height:1;font-size:8rem}.ui.modal>.actions{background:#f9fafb;padding:1rem 1rem;border-top:1px solid rgba(34,36,38,.15);text-align:right}.ui.modal .actions>.button{margin-left:.75em}@media only screen and (max-width:767px){.ui.modal{width:95%;margin:0 0 0 -47.5%}}@media only screen and (min-width:768px){.ui.modal{width:88%;margin:0 0 0 -44%}}@media only screen and (min-width:992px){.ui.modal{width:850px;margin:0 0 0 -425px}}@media only screen and (min-width:1200px){.ui.modal{width:900px;margin:0 0 0 -450px}}@media only screen and (min-width:1920px){.ui.modal{width:950px;margin:0 0 0 -475px}}@media only screen and (max-width:991px){.ui.modal>.header{padding-right:2.25rem}.ui.modal>.close{top:1.0535rem;right:1rem;color:rgba(0,0,0,.87)}}@media only screen and (max-width:767px){.ui.modal>.header{padding:.75rem 1rem!important;padding-right:2.25rem!important}.ui.modal>.content{display:block;padding:1rem!important}.ui.modal>.close{top:.5rem!important;right:.5rem!important}.ui.modal .image.content{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.modal .content>.image{display:block;max-width:100%;margin:0 auto!important;text-align:center;padding:0 0 1rem!important}.ui.modal>.content>.image>i.icon{font-size:5rem;text-align:center}.ui.modal .content>.description{display:block;width:100%!important;margin:0!important;padding:1rem 0!important;box-shadow:none}.ui.modal>.actions{padding:1rem 1rem 0!important}.ui.modal .actions>.button,.ui.modal .actions>.buttons{margin-bottom:1rem}}.ui.inverted.dimmer>.ui.modal{box-shadow:1px 3px 10px 2px rgba(0,0,0,.2)}.ui.basic.modal{background-color:transparent;border:none;border-radius:0;box-shadow:none!important;color:#fff}.ui.basic.modal>.actions,.ui.basic.modal>.content,.ui.basic.modal>.header{background-color:transparent}.ui.basic.modal>.header{color:#fff}.ui.basic.modal>.close{top:1rem;right:1.5rem}.ui.inverted.dimmer>.basic.modal{color:rgba(0,0,0,.87)}.ui.inverted.dimmer>.ui.basic.modal>.header{color:rgba(0,0,0,.85)}@media only screen and (max-width:991px){.ui.basic.modal>.close{color:#fff}}.ui.active.modal{display:block}.scrolling.dimmable.dimmed{overflow:hidden}.scrolling.dimmable.dimmed>.dimmer{overflow:auto;-webkit-overflow-scrolling:touch}.scrolling.dimmable>.dimmer{position:fixed}.modals.dimmer .ui.scrolling.modal{position:static!important;margin:3.5rem auto!important}.scrolling.undetached.dimmable.dimmed{overflow:auto;-webkit-overflow-scrolling:touch}.scrolling.undetached.dimmable.dimmed>.dimmer{overflow:hidden}.scrolling.undetached.dimmable .ui.scrolling.modal{position:absolute;left:50%;margin-top:3.5rem!important}.undetached.dimmable.dimmed>.pusher{z-index:auto}@media only screen and (max-width:991px){.modals.dimmer .ui.scrolling.modal{margin-top:1rem!important;margin-bottom:1rem!important}}.ui.fullscreen.modal{width:95%!important;left:2.5%!important;margin:1em auto}.ui.fullscreen.scrolling.modal{left:0!important}.ui.fullscreen.modal>.header{padding-right:2.25rem}.ui.fullscreen.modal>.close{top:1.0535rem;right:1rem;color:rgba(0,0,0,.87)}.ui.modal{font-size:1rem}.ui.small.modal>.header:not(.ui){font-size:1.3em}@media only screen and (max-width:767px){.ui.small.modal{width:95%;margin:0 0 0 -47.5%}}@media only screen and (min-width:768px){.ui.small.modal{width:70.4%;margin:0 0 0 -35.2%}}@media only screen and (min-width:992px){.ui.small.modal{width:680px;margin:0 0 0 -340px}}@media only screen and (min-width:1200px){.ui.small.modal{width:720px;margin:0 0 0 -360px}}@media only screen and (min-width:1920px){.ui.small.modal{width:760px;margin:0 0 0 -380px}}.ui.large.modal>.header{font-size:1.6em}@media only screen and (max-width:767px){.ui.large.modal{width:95%;margin:0 0 0 -47.5%}}@media only screen and (min-width:768px){.ui.large.modal{width:88%;margin:0 0 0 -44%}}@media only screen and (min-width:992px){.ui.large.modal{width:1020px;margin:0 0 0 -510px}}@media only screen and (min-width:1200px){.ui.large.modal{width:1080px;margin:0 0 0 -540px}}@media only screen and (min-width:1920px){.ui.large.modal{width:1140px;margin:0 0 0 -570px}} -------------------------------------------------------------------------------- /public/css/components/message.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Message 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.message{position:relative;min-height:1em;margin:1em 0;background:#f8f8f9;padding:1em 1.5em;line-height:1.4285em;color:rgba(0,0,0,.87);-webkit-transition:opacity .1s ease,color .1s ease,background .1s ease,box-shadow .1s ease;transition:opacity .1s ease,color .1s ease,background .1s ease,box-shadow .1s ease;border-radius:.28571429rem;box-shadow:0 0 0 1px rgba(34,36,38,.22) inset,0 0 0 0 transparent}.ui.message:first-child{margin-top:0}.ui.message:last-child{margin-bottom:0}.ui.message .header{display:block;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;margin:-.14285714em 0 0 0}.ui.message .header:not(.ui){font-size:1.14285714em}.ui.message p{opacity:.85;margin:.75em 0}.ui.message p:first-child{margin-top:0}.ui.message p:last-child{margin-bottom:0}.ui.message .header+p{margin-top:.25em}.ui.message .list:not(.ui){text-align:left;padding:0;opacity:.85;list-style-position:inside;margin:.5em 0 0}.ui.message .list:not(.ui):first-child{margin-top:0}.ui.message .list:not(.ui):last-child{margin-bottom:0}.ui.message .list:not(.ui) li{position:relative;list-style-type:none;margin:0 0 .3em 1em;padding:0}.ui.message .list:not(.ui) li:before{position:absolute;content:'•';left:-1em;height:100%;vertical-align:baseline}.ui.message .list:not(.ui) li:last-child{margin-bottom:0}.ui.message>.icon{margin-right:.6em}.ui.message>.close.icon{cursor:pointer;position:absolute;margin:0;top:.78575em;right:.5em;opacity:.7;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.message>.close.icon:hover{opacity:1}.ui.message>:first-child{margin-top:0}.ui.message>:last-child{margin-bottom:0}.ui.dropdown .menu>.message{margin:0 -1px}.ui.visible.visible.visible.visible.message{display:block}.ui.icon.visible.visible.visible.visible.message{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.hidden.hidden.hidden.hidden.message{display:none}.ui.compact.message{display:inline-block}.ui.attached.message{margin-bottom:-1px;border-radius:.28571429rem .28571429rem 0 0;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset;margin-left:-1px;margin-right:-1px}.ui.attached+.ui.attached.message:not(.top):not(.bottom){margin-top:-1px;border-radius:0}.ui.bottom.attached.message{margin-top:-1px;border-radius:0 0 .28571429rem .28571429rem;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset,0 1px 2px 0 rgba(34,36,38,.15)}.ui.bottom.attached.message:not(:last-child){margin-bottom:1em}.ui.attached.icon.message{width:auto}.ui.icon.message{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.icon.message>.icon:not(.close){display:block;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;line-height:1;vertical-align:middle;font-size:3em;opacity:.8}.ui.icon.message>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;vertical-align:middle}.ui.icon.message .icon:not(.close)+.content{padding-left:0}.ui.icon.message .circular.icon{width:1em}.ui.floating.message{box-shadow:0 0 0 1px rgba(34,36,38,.22) inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.black.message{background-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.positive.message{background-color:#fcfff5;color:#2c662d}.ui.attached.positive.message,.ui.positive.message{box-shadow:0 0 0 1px #a3c293 inset,0 0 0 0 transparent}.ui.positive.message .header{color:#1a531b}.ui.negative.message{background-color:#fff6f6;color:#9f3a38}.ui.attached.negative.message,.ui.negative.message{box-shadow:0 0 0 1px #e0b4b4 inset,0 0 0 0 transparent}.ui.negative.message .header{color:#912d2b}.ui.info.message{background-color:#f8ffff;color:#276f86}.ui.attached.info.message,.ui.info.message{box-shadow:0 0 0 1px #a9d5de inset,0 0 0 0 transparent}.ui.info.message .header{color:#0e566c}.ui.warning.message{background-color:#fffaf3;color:#573a08}.ui.attached.warning.message,.ui.warning.message{box-shadow:0 0 0 1px #c9ba9b inset,0 0 0 0 transparent}.ui.warning.message .header{color:#794b02}.ui.error.message{background-color:#fff6f6;color:#9f3a38}.ui.attached.error.message,.ui.error.message{box-shadow:0 0 0 1px #e0b4b4 inset,0 0 0 0 transparent}.ui.error.message .header{color:#912d2b}.ui.success.message{background-color:#fcfff5;color:#2c662d}.ui.attached.success.message,.ui.success.message{box-shadow:0 0 0 1px #a3c293 inset,0 0 0 0 transparent}.ui.success.message .header{color:#1a531b}.ui.black.message,.ui.inverted.message{background-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.red.message{background-color:#ffe8e6;color:#db2828;box-shadow:0 0 0 1px #db2828 inset,0 0 0 0 transparent}.ui.red.message .header{color:#c82121}.ui.orange.message{background-color:#ffedde;color:#f2711c;box-shadow:0 0 0 1px #f2711c inset,0 0 0 0 transparent}.ui.orange.message .header{color:#e7640d}.ui.yellow.message{background-color:#fff8db;color:#b58105;box-shadow:0 0 0 1px #b58105 inset,0 0 0 0 transparent}.ui.yellow.message .header{color:#9c6f04}.ui.olive.message{background-color:#fbfdef;color:#8abc1e;box-shadow:0 0 0 1px #8abc1e inset,0 0 0 0 transparent}.ui.olive.message .header{color:#7aa61a}.ui.green.message{background-color:#e5f9e7;color:#1ebc30;box-shadow:0 0 0 1px #1ebc30 inset,0 0 0 0 transparent}.ui.green.message .header{color:#1aa62a}.ui.teal.message{background-color:#e1f7f7;color:#10a3a3;box-shadow:0 0 0 1px #10a3a3 inset,0 0 0 0 transparent}.ui.teal.message .header{color:#0e8c8c}.ui.blue.message{background-color:#dff0ff;color:#2185d0;box-shadow:0 0 0 1px #2185d0 inset,0 0 0 0 transparent}.ui.blue.message .header{color:#1e77ba}.ui.violet.message{background-color:#eae7ff;color:#6435c9;box-shadow:0 0 0 1px #6435c9 inset,0 0 0 0 transparent}.ui.violet.message .header{color:#5a30b5}.ui.purple.message{background-color:#f6e7ff;color:#a333c8;box-shadow:0 0 0 1px #a333c8 inset,0 0 0 0 transparent}.ui.purple.message .header{color:#922eb4}.ui.pink.message{background-color:#ffe3fb;color:#e03997;box-shadow:0 0 0 1px #e03997 inset,0 0 0 0 transparent}.ui.pink.message .header{color:#dd238b}.ui.brown.message{background-color:#f1e2d3;color:#a5673f;box-shadow:0 0 0 1px #a5673f inset,0 0 0 0 transparent}.ui.brown.message .header{color:#935b38}.ui.mini.message{font-size:.78571429em}.ui.tiny.message{font-size:.85714286em}.ui.small.message{font-size:.92857143em}.ui.message{font-size:1em}.ui.large.message{font-size:1.14285714em}.ui.big.message{font-size:1.28571429em}.ui.huge.message{font-size:1.42857143em}.ui.massive.message{font-size:1.71428571em} -------------------------------------------------------------------------------- /public/css/components/accordion.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Accordion 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.accordion,.ui.accordion .accordion{max-width:100%}.ui.accordion .accordion{margin:1em 0 0;padding:0}.ui.accordion .accordion .title,.ui.accordion .title{cursor:pointer}.ui.accordion .title:not(.ui){padding:.5em 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;color:rgba(0,0,0,.87)}.ui.accordion .accordion .title~.content,.ui.accordion .title~.content{display:none}.ui.accordion:not(.styled) .accordion .title~.content:not(.ui),.ui.accordion:not(.styled) .title~.content:not(.ui){margin:'';padding:.5em 0 1em}.ui.accordion:not(.styled) .title~.content:not(.ui):last-child{padding-bottom:0}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{display:inline-block;float:none;opacity:1;width:1.25em;height:1em;margin:0 .25rem 0 0;padding:0;font-size:1em;-webkit-transition:opacity .1s ease,-webkit-transform .1s ease;transition:opacity .1s ease,-webkit-transform .1s ease;transition:transform .1s ease,opacity .1s ease;transition:transform .1s ease,opacity .1s ease,-webkit-transform .1s ease;vertical-align:baseline;-webkit-transform:none;transform:none}.ui.accordion.menu .item .title{display:block;padding:0}.ui.accordion.menu .item .title>.dropdown.icon{float:right;margin:.21425em 0 0 1em;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.ui.accordion .ui.header .dropdown.icon{font-size:1em;margin:0 .25rem 0 0}.ui.accordion .accordion .active.title .dropdown.icon,.ui.accordion .active.title .dropdown.icon{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ui.accordion.menu .item .active.title>.dropdown.icon{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ui.styled.accordion{width:600px}.ui.styled.accordion,.ui.styled.accordion .accordion{border-radius:.28571429rem;background:#fff;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15)}.ui.styled.accordion .accordion .title,.ui.styled.accordion .title{margin:0;padding:.75em 1em;color:rgba(0,0,0,.4);font-weight:700;border-top:1px solid rgba(34,36,38,.15);-webkit-transition:background .1s ease,color .1s ease;transition:background .1s ease,color .1s ease}.ui.styled.accordion .accordion .title:first-child,.ui.styled.accordion>.title:first-child{border-top:none}.ui.styled.accordion .accordion .content,.ui.styled.accordion .content{margin:0;padding:.5em 1em 1.5em}.ui.styled.accordion .accordion .content{padding:0;padding:.5em 1em 1.5em}.ui.styled.accordion .accordion .active.title,.ui.styled.accordion .accordion .title:hover,.ui.styled.accordion .active.title,.ui.styled.accordion .title:hover{background:0 0;color:rgba(0,0,0,.87)}.ui.styled.accordion .accordion .active.title,.ui.styled.accordion .accordion .title:hover{background:0 0;color:rgba(0,0,0,.87)}.ui.styled.accordion .active.title{background:0 0;color:rgba(0,0,0,.95)}.ui.styled.accordion .accordion .active.title{background:0 0;color:rgba(0,0,0,.95)}.ui.accordion .accordion .active.content,.ui.accordion .active.content{display:block}.ui.fluid.accordion,.ui.fluid.accordion .accordion{width:100%}.ui.inverted.accordion .title:not(.ui){color:rgba(255,255,255,.9)}@font-face{font-family:Accordion;src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfOIKAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zryj6HgAAAFwAAAAyGhlYWT/0IhHAAACOAAAADZoaGVhApkB5wAAAnAAAAAkaG10eAJuABIAAAKUAAAAGGxvY2EAjABWAAACrAAAAA5tYXhwAAgAFgAAArwAAAAgbmFtZfC1n04AAALcAAABPHBvc3QAAwAAAAAEGAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQASAEkAtwFuABMAADc0PwE2FzYXFh0BFAcGJwYvASY1EgaABQgHBQYGBQcIBYAG2wcGfwcBAQcECf8IBAcBAQd/BgYAAAAAAQAAAEkApQFuABMAADcRNDc2MzIfARYVFA8BBiMiJyY1AAUGBwgFgAYGgAUIBwYFWwEACAUGBoAFCAcFgAYGBQcAAAABAAAAAQAAqWYls18PPPUACwIAAAAAAM/9o+4AAAAAz/2j7gAAAAAAtwFuAAAACAACAAAAAAAAAAEAAAHg/+AAAAIAAAAAAAC3AAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAQAAAAC3ABIAtwAAAAAAAAAKABQAHgBCAGQAAAABAAAABgAUAAEAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype'),url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAASwAAoAAAAABGgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAS0AAAEtFpovuE9TLzIAAAIkAAAAYAAAAGAIIweQY21hcAAAAoQAAABMAAAATA984gpnYXNwAAAC0AAAAAgAAAAIAAAAEGhlYWQAAALYAAAANgAAADb/0IhHaGhlYQAAAxAAAAAkAAAAJAKZAedobXR4AAADNAAAABgAAAAYAm4AEm1heHAAAANMAAAABgAAAAYABlAAbmFtZQAAA1QAAAE8AAABPPC1n05wb3N0AAAEkAAAACAAAAAgAAMAAAEABAQAAQEBB3JhdGluZwABAgABADr4HAL4GwP4GAQeCgAZU/+Lix4KABlT/4uLDAeLa/iU+HQFHQAAAHkPHQAAAH4RHQAAAAkdAAABJBIABwEBBw0PERQZHnJhdGluZ3JhdGluZ3UwdTF1MjB1RjBEOXVGMERBAAACAYkABAAGAQEEBwoNVp38lA78lA78lA77lA773Z33bxWLkI2Qj44I9xT3FAWOj5CNkIuQi4+JjoePiI2Gi4YIi/uUBYuGiYeHiIiHh4mGi4aLho2Ijwj7FPcUBYeOiY+LkAgO+92L5hWL95QFi5CNkI6Oj4+PjZCLkIuQiY6HCPcU+xQFj4iNhouGi4aJh4eICPsU+xQFiIeGiYaLhouHjYePiI6Jj4uQCA74lBT4lBWLDAoAAAAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAEAADfYOJZfDzz1AAsCAAAAAADP/aPuAAAAAM/9o+4AAAAAALcBbgAAAAgAAgAAAAAAAAABAAAB4P/gAAACAAAAAAAAtwABAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAEAAAAAtwASALcAAAAAUAAABgAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff');font-weight:400;font-style:normal}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{font-family:Accordion;line-height:1;-webkit-backface-visibility:hidden;backface-visibility:hidden;font-weight:400;font-style:normal;text-align:center}.ui.accordion .accordion .title .dropdown.icon:before,.ui.accordion .title .dropdown.icon:before{content:'\f0da'} -------------------------------------------------------------------------------- /public/css/components/image.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Image 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Image 14 | *******************************/ 15 | 16 | .ui.image { 17 | position: relative; 18 | display: inline-block; 19 | vertical-align: middle; 20 | max-width: 100%; 21 | background-color: transparent; 22 | } 23 | img.ui.image { 24 | display: block; 25 | } 26 | .ui.image svg, 27 | .ui.image img { 28 | display: block; 29 | max-width: 100%; 30 | height: auto; 31 | } 32 | 33 | 34 | /******************************* 35 | States 36 | *******************************/ 37 | 38 | .ui.hidden.images, 39 | .ui.hidden.image { 40 | display: none; 41 | } 42 | .ui.hidden.transition.images, 43 | .ui.hidden.transition.image { 44 | display: block; 45 | visibility: hidden; 46 | } 47 | .ui.disabled.images, 48 | .ui.disabled.image { 49 | cursor: default; 50 | opacity: 0.45; 51 | } 52 | 53 | 54 | /******************************* 55 | Variations 56 | *******************************/ 57 | 58 | 59 | /*-------------- 60 | Inline 61 | ---------------*/ 62 | 63 | .ui.inline.image, 64 | .ui.inline.image svg, 65 | .ui.inline.image img { 66 | display: inline-block; 67 | } 68 | 69 | /*------------------ 70 | Vertical Aligned 71 | -------------------*/ 72 | 73 | .ui.top.aligned.images .image, 74 | .ui.top.aligned.image, 75 | .ui.top.aligned.image svg, 76 | .ui.top.aligned.image img { 77 | display: inline-block; 78 | vertical-align: top; 79 | } 80 | .ui.middle.aligned.images .image, 81 | .ui.middle.aligned.image, 82 | .ui.middle.aligned.image svg, 83 | .ui.middle.aligned.image img { 84 | display: inline-block; 85 | vertical-align: middle; 86 | } 87 | .ui.bottom.aligned.images .image, 88 | .ui.bottom.aligned.image, 89 | .ui.bottom.aligned.image svg, 90 | .ui.bottom.aligned.image img { 91 | display: inline-block; 92 | vertical-align: bottom; 93 | } 94 | 95 | /*-------------- 96 | Rounded 97 | ---------------*/ 98 | 99 | .ui.rounded.images .image, 100 | .ui.rounded.image, 101 | .ui.rounded.images .image > *, 102 | .ui.rounded.image > * { 103 | border-radius: 0.3125em; 104 | } 105 | 106 | /*-------------- 107 | Bordered 108 | ---------------*/ 109 | 110 | .ui.bordered.images .image, 111 | .ui.bordered.images img, 112 | .ui.bordered.images svg, 113 | .ui.bordered.image img, 114 | .ui.bordered.image svg, 115 | img.ui.bordered.image { 116 | border: 1px solid rgba(0, 0, 0, 0.1); 117 | } 118 | 119 | /*-------------- 120 | Circular 121 | ---------------*/ 122 | 123 | .ui.circular.images, 124 | .ui.circular.image { 125 | overflow: hidden; 126 | } 127 | .ui.circular.images .image, 128 | .ui.circular.image, 129 | .ui.circular.images .image > *, 130 | .ui.circular.image > * { 131 | border-radius: 500rem; 132 | } 133 | 134 | /*-------------- 135 | Fluid 136 | ---------------*/ 137 | 138 | .ui.fluid.images, 139 | .ui.fluid.image, 140 | .ui.fluid.images img, 141 | .ui.fluid.images svg, 142 | .ui.fluid.image svg, 143 | .ui.fluid.image img { 144 | display: block; 145 | width: 100%; 146 | height: auto; 147 | } 148 | 149 | /*-------------- 150 | Avatar 151 | ---------------*/ 152 | 153 | .ui.avatar.images .image, 154 | .ui.avatar.images img, 155 | .ui.avatar.images svg, 156 | .ui.avatar.image img, 157 | .ui.avatar.image svg, 158 | .ui.avatar.image { 159 | margin-right: 0.25em; 160 | display: inline-block; 161 | width: 2em; 162 | height: 2em; 163 | border-radius: 500rem; 164 | } 165 | 166 | /*------------------- 167 | Spaced 168 | --------------------*/ 169 | 170 | .ui.spaced.image { 171 | display: inline-block !important; 172 | margin-left: 0.5em; 173 | margin-right: 0.5em; 174 | } 175 | .ui[class*="left spaced"].image { 176 | margin-left: 0.5em; 177 | margin-right: 0em; 178 | } 179 | .ui[class*="right spaced"].image { 180 | margin-left: 0em; 181 | margin-right: 0.5em; 182 | } 183 | 184 | /*------------------- 185 | Floated 186 | --------------------*/ 187 | 188 | .ui.floated.image, 189 | .ui.floated.images { 190 | float: left; 191 | margin-right: 1em; 192 | margin-bottom: 1em; 193 | } 194 | .ui.right.floated.images, 195 | .ui.right.floated.image { 196 | float: right; 197 | margin-right: 0em; 198 | margin-bottom: 1em; 199 | margin-left: 1em; 200 | } 201 | .ui.floated.images:last-child, 202 | .ui.floated.image:last-child { 203 | margin-bottom: 0em; 204 | } 205 | .ui.centered.images, 206 | .ui.centered.image { 207 | margin-left: auto; 208 | margin-right: auto; 209 | } 210 | 211 | /*-------------- 212 | Sizes 213 | ---------------*/ 214 | 215 | .ui.mini.images .image, 216 | .ui.mini.images img, 217 | .ui.mini.images svg, 218 | .ui.mini.image { 219 | width: 35px; 220 | height: auto; 221 | font-size: 0.78571429rem; 222 | } 223 | .ui.tiny.images .image, 224 | .ui.tiny.images img, 225 | .ui.tiny.images svg, 226 | .ui.tiny.image { 227 | width: 80px; 228 | height: auto; 229 | font-size: 0.85714286rem; 230 | } 231 | .ui.small.images .image, 232 | .ui.small.images img, 233 | .ui.small.images svg, 234 | .ui.small.image { 235 | width: 150px; 236 | height: auto; 237 | font-size: 0.92857143rem; 238 | } 239 | .ui.medium.images .image, 240 | .ui.medium.images img, 241 | .ui.medium.images svg, 242 | .ui.medium.image { 243 | width: 300px; 244 | height: auto; 245 | font-size: 1rem; 246 | } 247 | .ui.large.images .image, 248 | .ui.large.images img, 249 | .ui.large.images svg, 250 | .ui.large.image { 251 | width: 450px; 252 | height: auto; 253 | font-size: 1.14285714rem; 254 | } 255 | .ui.big.images .image, 256 | .ui.big.images img, 257 | .ui.big.images svg, 258 | .ui.big.image { 259 | width: 600px; 260 | height: auto; 261 | font-size: 1.28571429rem; 262 | } 263 | .ui.huge.images .image, 264 | .ui.huge.images img, 265 | .ui.huge.images svg, 266 | .ui.huge.image { 267 | width: 800px; 268 | height: auto; 269 | font-size: 1.42857143rem; 270 | } 271 | .ui.massive.images .image, 272 | .ui.massive.images img, 273 | .ui.massive.images svg, 274 | .ui.massive.image { 275 | width: 960px; 276 | height: auto; 277 | font-size: 1.71428571rem; 278 | } 279 | 280 | 281 | /******************************* 282 | Groups 283 | *******************************/ 284 | 285 | .ui.images { 286 | font-size: 0em; 287 | margin: 0em -0.25rem 0rem; 288 | } 289 | .ui.images .image, 290 | .ui.images img, 291 | .ui.images svg { 292 | display: inline-block; 293 | margin: 0em 0.25rem 0.5rem; 294 | } 295 | 296 | 297 | /******************************* 298 | Theme Overrides 299 | *******************************/ 300 | 301 | 302 | 303 | /******************************* 304 | Site Overrides 305 | *******************************/ 306 | 307 | -------------------------------------------------------------------------------- /public/css/components/accordion.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Accordion 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | !function(e,n,t,i){"use strict";n="undefined"!=typeof n&&n.Math==Math?n:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.accordion=function(t){var o,a=e(this),s=(new Date).getTime(),r=[],c=arguments[0],l="string"==typeof c,u=[].slice.call(arguments,1);n.requestAnimationFrame||n.mozRequestAnimationFrame||n.webkitRequestAnimationFrame||n.msRequestAnimationFrame||function(e){setTimeout(e,0)};return a.each(function(){var d,g,f=e.isPlainObject(t)?e.extend(!0,{},e.fn.accordion.settings,t):e.extend({},e.fn.accordion.settings),m=f.className,p=f.namespace,h=f.selector,v=f.error,b="."+p,y="module-"+p,C=a.selector||"",O=e(this),x=O.find(h.title),F=O.find(h.content),A=this,T=O.data(y);g={initialize:function(){g.debug("Initializing",O),g.bind.events(),f.observeChanges&&g.observeChanges(),g.instantiate()},instantiate:function(){T=g,O.data(y,g)},destroy:function(){g.debug("Destroying previous instance",O),O.off(b).removeData(y)},refresh:function(){x=O.find(h.title),F=O.find(h.content)},observeChanges:function(){"MutationObserver"in n&&(d=new MutationObserver(function(e){g.debug("DOM tree modified, updating selector cache"),g.refresh()}),d.observe(A,{childList:!0,subtree:!0}),g.debug("Setting up mutation observer",d))},bind:{events:function(){g.debug("Binding delegated events"),O.on(f.on+b,h.trigger,g.event.click)}},event:{click:function(){g.toggle.call(this)}},toggle:function(n){var t=n!==i?"number"==typeof n?x.eq(n):e(n).closest(h.title):e(this).closest(h.title),o=t.next(F),a=o.hasClass(m.animating),s=o.hasClass(m.active),r=s&&!a,c=!s&&a;g.debug("Toggling visibility of content",t),r||c?f.collapsible?g.close.call(t):g.debug("Cannot close accordion content collapsing is disabled"):g.open.call(t)},open:function(n){var t=n!==i?"number"==typeof n?x.eq(n):e(n).closest(h.title):e(this).closest(h.title),o=t.next(F),a=o.hasClass(m.animating),s=o.hasClass(m.active),r=s||a;return r?void g.debug("Accordion already open, skipping",o):(g.debug("Opening accordion content",t),f.onOpening.call(o),f.exclusive&&g.closeOthers.call(t),t.addClass(m.active),o.stop(!0,!0).addClass(m.animating),f.animateChildren&&(e.fn.transition!==i&&O.transition("is supported")?o.children().transition({animation:"fade in",queue:!1,useFailSafe:!0,debug:f.debug,verbose:f.verbose,duration:f.duration}):o.children().stop(!0,!0).animate({opacity:1},f.duration,g.resetOpacity)),void o.slideDown(f.duration,f.easing,function(){o.removeClass(m.animating).addClass(m.active),g.reset.display.call(this),f.onOpen.call(this),f.onChange.call(this)}))},close:function(n){var t=n!==i?"number"==typeof n?x.eq(n):e(n).closest(h.title):e(this).closest(h.title),o=t.next(F),a=o.hasClass(m.animating),s=o.hasClass(m.active),r=!s&&a,c=s&&a;!s&&!r||c||(g.debug("Closing accordion content",o),f.onClosing.call(o),t.removeClass(m.active),o.stop(!0,!0).addClass(m.animating),f.animateChildren&&(e.fn.transition!==i&&O.transition("is supported")?o.children().transition({animation:"fade out",queue:!1,useFailSafe:!0,debug:f.debug,verbose:f.verbose,duration:f.duration}):o.children().stop(!0,!0).animate({opacity:0},f.duration,g.resetOpacity)),o.slideUp(f.duration,f.easing,function(){o.removeClass(m.animating).removeClass(m.active),g.reset.display.call(this),f.onClose.call(this),f.onChange.call(this)}))},closeOthers:function(n){var t,o,a,s=n!==i?x.eq(n):e(this).closest(h.title),r=s.parents(h.content).prev(h.title),c=s.closest(h.accordion),l=h.title+"."+m.active+":visible",u=h.content+"."+m.active+":visible";f.closeNested?(t=c.find(l).not(r),a=t.next(F)):(t=c.find(l).not(r),o=c.find(u).find(l).not(r),t=t.not(o),a=t.next(F)),t.length>0&&(g.debug("Exclusive enabled, closing other content",t),t.removeClass(m.active),a.removeClass(m.animating).stop(!0,!0),f.animateChildren&&(e.fn.transition!==i&&O.transition("is supported")?a.children().transition({animation:"fade out",useFailSafe:!0,debug:f.debug,verbose:f.verbose,duration:f.duration}):a.children().stop(!0,!0).animate({opacity:0},f.duration,g.resetOpacity)),a.slideUp(f.duration,f.easing,function(){e(this).removeClass(m.active),g.reset.display.call(this)}))},reset:{display:function(){g.verbose("Removing inline display from element",this),e(this).css("display",""),""===e(this).attr("style")&&e(this).attr("style","").removeAttr("style")},opacity:function(){g.verbose("Removing inline opacity from element",this),e(this).css("opacity",""),""===e(this).attr("style")&&e(this).attr("style","").removeAttr("style")}},setting:function(n,t){if(g.debug("Changing setting",n,t),e.isPlainObject(n))e.extend(!0,f,n);else{if(t===i)return f[n];e.isPlainObject(f[n])?e.extend(!0,f[n],t):f[n]=t}},internal:function(n,t){return g.debug("Changing internal",n,t),t===i?g[n]:void(e.isPlainObject(n)?e.extend(!0,g,n):g[n]=t)},debug:function(){!f.silent&&f.debug&&(f.performance?g.performance.log(arguments):(g.debug=Function.prototype.bind.call(console.info,console,f.name+":"),g.debug.apply(console,arguments)))},verbose:function(){!f.silent&&f.verbose&&f.debug&&(f.performance?g.performance.log(arguments):(g.verbose=Function.prototype.bind.call(console.info,console,f.name+":"),g.verbose.apply(console,arguments)))},error:function(){f.silent||(g.error=Function.prototype.bind.call(console.error,console,f.name+":"),g.error.apply(console,arguments))},performance:{log:function(e){var n,t,i;f.performance&&(n=(new Date).getTime(),i=s||n,t=n-i,s=n,r.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:A,"Execution Time":t})),clearTimeout(g.performance.timer),g.performance.timer=setTimeout(g.performance.display,500)},display:function(){var n=f.name+":",t=0;s=!1,clearTimeout(g.performance.timer),e.each(r,function(e,n){t+=n["Execution Time"]}),n+=" "+t+"ms",C&&(n+=" '"+C+"'"),(console.group!==i||console.table!==i)&&r.length>0&&(console.groupCollapsed(n),console.table?console.table(r):e.each(r,function(e,n){console.log(n.Name+": "+n["Execution Time"]+"ms")}),console.groupEnd()),r=[]}},invoke:function(n,t,a){var s,r,c,l=T;return t=t||u,a=A||a,"string"==typeof n&&l!==i&&(n=n.split(/[\. ]/),s=n.length-1,e.each(n,function(t,o){var a=t!=s?o+n[t+1].charAt(0).toUpperCase()+n[t+1].slice(1):n;if(e.isPlainObject(l[a])&&t!=s)l=l[a];else{if(l[a]!==i)return r=l[a],!1;if(!e.isPlainObject(l[o])||t==s)return l[o]!==i?(r=l[o],!1):(g.error(v.method,n),!1);l=l[o]}})),e.isFunction(r)?c=r.apply(a,t):r!==i&&(c=r),e.isArray(o)?o.push(c):o!==i?o=[o,c]:c!==i&&(o=c),r}},l?(T===i&&g.initialize(),g.invoke(c)):(T!==i&&T.invoke("destroy"),g.initialize())}),o!==i?o:this},e.fn.accordion.settings={name:"Accordion",namespace:"accordion",silent:!1,debug:!1,verbose:!1,performance:!0,on:"click",observeChanges:!0,exclusive:!0,collapsible:!0,closeNested:!1,animateChildren:!0,duration:350,easing:"easeOutQuad",onOpening:function(){},onOpen:function(){},onClosing:function(){},onClose:function(){},onChange:function(){},error:{method:"The method you called is not defined"},className:{active:"active",animating:"animating"},selector:{accordion:".accordion",title:".title",trigger:".title",content:".content"}},e.extend(e.easing,{easeOutQuad:function(e,n,t,i,o){return-i*(n/=o)*(n-2)+t}})}(jQuery,window,document); -------------------------------------------------------------------------------- /public/css/components/feed.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Feed 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Activity Feed 14 | *******************************/ 15 | 16 | .ui.feed { 17 | margin: 1em 0em; 18 | } 19 | .ui.feed:first-child { 20 | margin-top: 0em; 21 | } 22 | .ui.feed:last-child { 23 | margin-bottom: 0em; 24 | } 25 | 26 | 27 | /******************************* 28 | Content 29 | *******************************/ 30 | 31 | 32 | /* Event */ 33 | .ui.feed > .event { 34 | display: -webkit-box; 35 | display: -ms-flexbox; 36 | display: flex; 37 | -webkit-box-orient: horizontal; 38 | -webkit-box-direction: normal; 39 | -ms-flex-direction: row; 40 | flex-direction: row; 41 | width: 100%; 42 | padding: 0.21428571rem 0em; 43 | margin: 0em; 44 | background: none; 45 | border-top: none; 46 | } 47 | .ui.feed > .event:first-child { 48 | border-top: 0px; 49 | padding-top: 0em; 50 | } 51 | .ui.feed > .event:last-child { 52 | padding-bottom: 0em; 53 | } 54 | 55 | /* Event Label */ 56 | .ui.feed > .event > .label { 57 | display: block; 58 | -webkit-box-flex: 0; 59 | -ms-flex: 0 0 auto; 60 | flex: 0 0 auto; 61 | width: 2.5em; 62 | height: auto; 63 | -ms-flex-item-align: stretch; 64 | -ms-grid-row-align: stretch; 65 | align-self: stretch; 66 | text-align: left; 67 | } 68 | .ui.feed > .event > .label .icon { 69 | opacity: 1; 70 | font-size: 1.5em; 71 | width: 100%; 72 | padding: 0.25em; 73 | background: none; 74 | border: none; 75 | border-radius: none; 76 | color: rgba(0, 0, 0, 0.6); 77 | } 78 | .ui.feed > .event > .label img { 79 | width: 100%; 80 | height: auto; 81 | border-radius: 500rem; 82 | } 83 | .ui.feed > .event > .label + .content { 84 | margin: 0.5em 0em 0.35714286em 1.14285714em; 85 | } 86 | 87 | /*-------------- 88 | Content 89 | ---------------*/ 90 | 91 | 92 | /* Content */ 93 | .ui.feed > .event > .content { 94 | display: block; 95 | -webkit-box-flex: 1; 96 | -ms-flex: 1 1 auto; 97 | flex: 1 1 auto; 98 | -ms-flex-item-align: stretch; 99 | -ms-grid-row-align: stretch; 100 | align-self: stretch; 101 | text-align: left; 102 | word-wrap: break-word; 103 | } 104 | .ui.feed > .event:last-child > .content { 105 | padding-bottom: 0em; 106 | } 107 | 108 | /* Link */ 109 | .ui.feed > .event > .content a { 110 | cursor: pointer; 111 | } 112 | 113 | /*-------------- 114 | Date 115 | ---------------*/ 116 | 117 | .ui.feed > .event > .content .date { 118 | margin: -0.5rem 0em 0em; 119 | padding: 0em; 120 | font-weight: normal; 121 | font-size: 1em; 122 | font-style: normal; 123 | color: rgba(0, 0, 0, 0.4); 124 | } 125 | 126 | /*-------------- 127 | Summary 128 | ---------------*/ 129 | 130 | .ui.feed > .event > .content .summary { 131 | margin: 0em; 132 | font-size: 1em; 133 | font-weight: bold; 134 | color: rgba(0, 0, 0, 0.87); 135 | } 136 | 137 | /* Summary Image */ 138 | .ui.feed > .event > .content .summary img { 139 | display: inline-block; 140 | width: auto; 141 | height: 10em; 142 | margin: -0.25em 0.25em 0em 0em; 143 | border-radius: 0.25em; 144 | vertical-align: middle; 145 | } 146 | 147 | /*-------------- 148 | User 149 | ---------------*/ 150 | 151 | .ui.feed > .event > .content .user { 152 | display: inline-block; 153 | font-weight: bold; 154 | margin-right: 0em; 155 | vertical-align: baseline; 156 | } 157 | .ui.feed > .event > .content .user img { 158 | margin: -0.25em 0.25em 0em 0em; 159 | width: auto; 160 | height: 10em; 161 | vertical-align: middle; 162 | } 163 | 164 | /*-------------- 165 | Inline Date 166 | ---------------*/ 167 | 168 | 169 | /* Date inside Summary */ 170 | .ui.feed > .event > .content .summary > .date { 171 | display: inline-block; 172 | float: none; 173 | font-weight: normal; 174 | font-size: 0.85714286em; 175 | font-style: normal; 176 | margin: 0em 0em 0em 0.5em; 177 | padding: 0em; 178 | color: rgba(0, 0, 0, 0.4); 179 | } 180 | 181 | /*-------------- 182 | Extra Summary 183 | ---------------*/ 184 | 185 | .ui.feed > .event > .content .extra { 186 | margin: 0.5em 0em 0em; 187 | background: none; 188 | padding: 0em; 189 | color: rgba(0, 0, 0, 0.87); 190 | } 191 | 192 | /* Images */ 193 | .ui.feed > .event > .content .extra.images img { 194 | display: inline-block; 195 | margin: 0em 0.25em 0em 0em; 196 | width: 6em; 197 | } 198 | 199 | /* Text */ 200 | .ui.feed > .event > .content .extra.text { 201 | padding: 0em; 202 | border-left: none; 203 | font-size: 1em; 204 | max-width: 500px; 205 | line-height: 1.4285em; 206 | } 207 | 208 | /*-------------- 209 | Meta 210 | ---------------*/ 211 | 212 | .ui.feed > .event > .content .meta { 213 | display: inline-block; 214 | font-size: 0.85714286em; 215 | margin: 0.5em 0em 0em; 216 | background: none; 217 | border: none; 218 | border-radius: 0; 219 | box-shadow: none; 220 | padding: 0em; 221 | color: rgba(0, 0, 0, 0.6); 222 | } 223 | .ui.feed > .event > .content .meta > * { 224 | position: relative; 225 | margin-left: 0.75em; 226 | } 227 | .ui.feed > .event > .content .meta > *:after { 228 | content: ''; 229 | color: rgba(0, 0, 0, 0.2); 230 | top: 0em; 231 | left: -1em; 232 | opacity: 1; 233 | position: absolute; 234 | vertical-align: top; 235 | } 236 | .ui.feed > .event > .content .meta .like { 237 | color: ''; 238 | -webkit-transition: 0.2s color ease; 239 | transition: 0.2s color ease; 240 | } 241 | .ui.feed > .event > .content .meta .like:hover .icon { 242 | color: #FF2733; 243 | } 244 | .ui.feed > .event > .content .meta .active.like .icon { 245 | color: #EF404A; 246 | } 247 | 248 | /* First element */ 249 | .ui.feed > .event > .content .meta > :first-child { 250 | margin-left: 0em; 251 | } 252 | .ui.feed > .event > .content .meta > :first-child::after { 253 | display: none; 254 | } 255 | 256 | /* Action */ 257 | .ui.feed > .event > .content .meta a, 258 | .ui.feed > .event > .content .meta > .icon { 259 | cursor: pointer; 260 | opacity: 1; 261 | color: rgba(0, 0, 0, 0.5); 262 | -webkit-transition: color 0.1s ease; 263 | transition: color 0.1s ease; 264 | } 265 | .ui.feed > .event > .content .meta a:hover, 266 | .ui.feed > .event > .content .meta a:hover .icon, 267 | .ui.feed > .event > .content .meta > .icon:hover { 268 | color: rgba(0, 0, 0, 0.95); 269 | } 270 | 271 | 272 | /******************************* 273 | Variations 274 | *******************************/ 275 | 276 | .ui.small.feed { 277 | font-size: 0.92857143rem; 278 | } 279 | .ui.feed { 280 | font-size: 1rem; 281 | } 282 | .ui.large.feed { 283 | font-size: 1.14285714rem; 284 | } 285 | 286 | 287 | /******************************* 288 | Theme Overrides 289 | *******************************/ 290 | 291 | 292 | 293 | /******************************* 294 | User Variable Overrides 295 | *******************************/ 296 | 297 | -------------------------------------------------------------------------------- /public/css/components/progress.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 2.2.9 - Progress Bar 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.progress{position:relative;display:block;max-width:100%;border:none;margin:1em 0 2.5em;box-shadow:none;background:rgba(0,0,0,.1);padding:0;border-radius:.28571429rem}.ui.progress:first-child{margin:0 0 2.5em}.ui.progress:last-child{margin:0 0 1.5em}.ui.progress .bar{display:block;line-height:1;position:relative;width:0;min-width:2em;background:#888;border-radius:.28571429rem;-webkit-transition:width .1s ease,background-color .1s ease;transition:width .1s ease,background-color .1s ease}.ui.progress .bar>.progress{white-space:nowrap;position:absolute;width:auto;font-size:.92857143em;top:50%;right:.5em;left:auto;bottom:auto;color:rgba(255,255,255,.7);text-shadow:none;margin-top:-.5em;font-weight:700;text-align:left}.ui.progress>.label{position:absolute;width:100%;font-size:1em;top:100%;right:auto;left:0;bottom:auto;color:rgba(0,0,0,.87);font-weight:700;text-shadow:none;margin-top:.2em;text-align:center;-webkit-transition:color .4s ease;transition:color .4s ease}.ui.indicating.progress[data-percent^="1"] .bar,.ui.indicating.progress[data-percent^="2"] .bar{background-color:#d95c5c}.ui.indicating.progress[data-percent^="3"] .bar{background-color:#efbc72}.ui.indicating.progress[data-percent^="4"] .bar,.ui.indicating.progress[data-percent^="5"] .bar{background-color:#e6bb48}.ui.indicating.progress[data-percent^="6"] .bar{background-color:#ddc928}.ui.indicating.progress[data-percent^="7"] .bar,.ui.indicating.progress[data-percent^="8"] .bar{background-color:#b4d95c}.ui.indicating.progress[data-percent^="100"] .bar,.ui.indicating.progress[data-percent^="9"] .bar{background-color:#66da81}.ui.indicating.progress[data-percent^="1"] .label,.ui.indicating.progress[data-percent^="2"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="3"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="4"] .label,.ui.indicating.progress[data-percent^="5"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="6"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="7"] .label,.ui.indicating.progress[data-percent^="8"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="100"] .label,.ui.indicating.progress[data-percent^="9"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent="1"] .bar,.ui.indicating.progress[data-percent="2"] .bar,.ui.indicating.progress[data-percent="3"] .bar,.ui.indicating.progress[data-percent="4"] .bar,.ui.indicating.progress[data-percent="5"] .bar,.ui.indicating.progress[data-percent="6"] .bar,.ui.indicating.progress[data-percent="7"] .bar,.ui.indicating.progress[data-percent="8"] .bar,.ui.indicating.progress[data-percent="9"] .bar{background-color:#d95c5c}.ui.indicating.progress[data-percent="1"] .label,.ui.indicating.progress[data-percent="2"] .label,.ui.indicating.progress[data-percent="3"] .label,.ui.indicating.progress[data-percent="4"] .label,.ui.indicating.progress[data-percent="5"] .label,.ui.indicating.progress[data-percent="6"] .label,.ui.indicating.progress[data-percent="7"] .label,.ui.indicating.progress[data-percent="8"] .label,.ui.indicating.progress[data-percent="9"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress.success .label{color:#1a531b}.ui.progress.success .bar{background-color:#21ba45!important}.ui.progress.success .bar,.ui.progress.success .bar::after{-webkit-animation:none!important;animation:none!important}.ui.progress.success>.label{color:#1a531b}.ui.progress.warning .bar{background-color:#f2c037!important}.ui.progress.warning .bar,.ui.progress.warning .bar::after{-webkit-animation:none!important;animation:none!important}.ui.progress.warning>.label{color:#794b02}.ui.progress.error .bar{background-color:#db2828!important}.ui.progress.error .bar,.ui.progress.error .bar::after{-webkit-animation:none!important;animation:none!important}.ui.progress.error>.label{color:#912d2b}.ui.active.progress .bar{position:relative;min-width:2em}.ui.active.progress .bar::after{content:'';opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;background:#fff;border-radius:.28571429rem;-webkit-animation:progress-active 2s ease infinite;animation:progress-active 2s ease infinite}@-webkit-keyframes progress-active{0%{opacity:.3;width:0}100%{opacity:0;width:100%}}@keyframes progress-active{0%{opacity:.3;width:0}100%{opacity:0;width:100%}}.ui.disabled.progress{opacity:.35}.ui.disabled.progress .bar,.ui.disabled.progress .bar::after{-webkit-animation:none!important;animation:none!important}.ui.inverted.progress{background:rgba(255,255,255,.08);border:none}.ui.inverted.progress .bar{background:#888}.ui.inverted.progress .bar>.progress{color:#f9fafb}.ui.inverted.progress>.label{color:#fff}.ui.inverted.progress.success>.label{color:#21ba45}.ui.inverted.progress.warning>.label{color:#f2c037}.ui.inverted.progress.error>.label{color:#db2828}.ui.progress.attached{background:0 0;position:relative;border:none;margin:0}.ui.progress.attached,.ui.progress.attached .bar{display:block;height:.2rem;padding:0;overflow:hidden;border-radius:0 0 .28571429rem .28571429rem}.ui.progress.attached .bar{border-radius:0}.ui.progress.top.attached,.ui.progress.top.attached .bar{top:0;border-radius:.28571429rem .28571429rem 0 0}.ui.progress.top.attached .bar{border-radius:0}.ui.card>.ui.attached.progress,.ui.segment>.ui.attached.progress{position:absolute;top:auto;left:0;bottom:100%;width:100%}.ui.card>.ui.bottom.attached.progress,.ui.segment>.ui.bottom.attached.progress{top:100%;bottom:auto}.ui.red.progress .bar{background-color:#db2828}.ui.red.inverted.progress .bar{background-color:#ff695e}.ui.orange.progress .bar{background-color:#f2711c}.ui.orange.inverted.progress .bar{background-color:#ff851b}.ui.yellow.progress .bar{background-color:#fbbd08}.ui.yellow.inverted.progress .bar{background-color:#ffe21f}.ui.olive.progress .bar{background-color:#b5cc18}.ui.olive.inverted.progress .bar{background-color:#d9e778}.ui.green.progress .bar{background-color:#21ba45}.ui.green.inverted.progress .bar{background-color:#2ecc40}.ui.teal.progress .bar{background-color:#00b5ad}.ui.teal.inverted.progress .bar{background-color:#6dffff}.ui.blue.progress .bar{background-color:#2185d0}.ui.blue.inverted.progress .bar{background-color:#54c8ff}.ui.violet.progress .bar{background-color:#6435c9}.ui.violet.inverted.progress .bar{background-color:#a291fb}.ui.purple.progress .bar{background-color:#a333c8}.ui.purple.inverted.progress .bar{background-color:#dc73ff}.ui.pink.progress .bar{background-color:#e03997}.ui.pink.inverted.progress .bar{background-color:#ff8edf}.ui.brown.progress .bar{background-color:#a5673f}.ui.brown.inverted.progress .bar{background-color:#d67c1c}.ui.grey.progress .bar{background-color:#767676}.ui.grey.inverted.progress .bar{background-color:#dcddde}.ui.black.progress .bar{background-color:#1b1c1d}.ui.black.inverted.progress .bar{background-color:#545454}.ui.tiny.progress{font-size:.85714286rem}.ui.tiny.progress .bar{height:.5em}.ui.small.progress{font-size:.92857143rem}.ui.small.progress .bar{height:1em}.ui.progress{font-size:1rem}.ui.progress .bar{height:1.75em}.ui.large.progress{font-size:1.14285714rem}.ui.large.progress .bar{height:2.5em}.ui.big.progress{font-size:1.28571429rem}.ui.big.progress .bar{height:3.5em} --------------------------------------------------------------------------------