├── public ├── img │ ├── bg.jpg │ ├── TK2.png │ ├── bug.png │ ├── talk.png │ ├── about.png │ ├── bug (1).png │ ├── post n.png │ ├── write.png │ ├── about (1).png │ ├── icon post.png │ ├── talk (1).png │ └── write (1).png ├── favicon.ico ├── src │ ├── store │ │ └── store.js │ ├── component │ │ ├── pleaseWait.js │ │ ├── posts │ │ │ ├── write.js │ │ │ ├── post.js │ │ │ ├── posts.js │ │ │ ├── comment.js │ │ │ ├── inputArea.js │ │ │ └── article.js │ │ ├── talk │ │ │ └── talk.js │ │ ├── nav.js │ │ ├── sign │ │ │ └── signInput.js │ │ └── about │ │ │ └── about.js │ ├── reducer │ │ ├── getMessages.js │ │ ├── loginState.js │ │ ├── index.js │ │ └── getData.js │ ├── route │ │ ├── talk.js │ │ ├── post.js │ │ ├── about.js │ │ ├── posts.js │ │ ├── write.js │ │ ├── update.js │ │ ├── signInput.js │ │ └── root.js │ ├── js │ │ ├── ajaxReturn.js │ │ └── getCookie.js │ ├── index.js │ └── action │ │ └── index.js ├── index.html ├── css │ ├── index.css │ └── about.css └── build │ ├── About.6577d.chunk.js │ ├── Nav.f69a4.chunk.js │ └── SignInput.d5958.chunk.js ├── config.js ├── routes ├── index.js ├── comments.js ├── sign.js └── posts.js ├── models ├── users.js ├── messages.js ├── comments.js └── posts.js ├── webpack.config.js ├── mongo └── mongo.js ├── package.json ├── README.md └── server.js /public/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/bg.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/img/TK2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/TK2.png -------------------------------------------------------------------------------- /public/img/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/bug.png -------------------------------------------------------------------------------- /public/img/talk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/talk.png -------------------------------------------------------------------------------- /public/img/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/about.png -------------------------------------------------------------------------------- /public/img/bug (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/bug (1).png -------------------------------------------------------------------------------- /public/img/post n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/post n.png -------------------------------------------------------------------------------- /public/img/write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/write.png -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | url: "http://localhost/", 3 | talkURL: "http://localhost:3001/" 4 | } -------------------------------------------------------------------------------- /public/img/about (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/about (1).png -------------------------------------------------------------------------------- /public/img/icon post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/icon post.png -------------------------------------------------------------------------------- /public/img/talk (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/talk (1).png -------------------------------------------------------------------------------- /public/img/write (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangkai123456/blog/HEAD/public/img/write (1).png -------------------------------------------------------------------------------- /public/src/store/store.js: -------------------------------------------------------------------------------- 1 | import {createStore} from 'redux' 2 | import reducer from "../reducer/index.js" 3 | 4 | export const store=createStore(reducer) -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | module.exports=function(app){ 2 | app.use("/sign",require("./sign.js")) 3 | app.use("/posts",require("./posts.js")) 4 | app.use("/comments",require("./comments.js")) 5 | } -------------------------------------------------------------------------------- /public/src/component/pleaseWait.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | 3 | export default class PlaseWait extends React.Component{ 4 | render(){ 5 | return ( 6 |
敬请期待
7 | ) 8 | } 9 | } -------------------------------------------------------------------------------- /public/src/reducer/getMessages.js: -------------------------------------------------------------------------------- 1 | import {GET_MESSAGES} from '../action/index.js' 2 | 3 | export default function getMessages(state=[],action){ 4 | switch (action.type){ 5 | case GET_MESSAGES: 6 | return action.data; 7 | default: 8 | return state 9 | } 10 | } -------------------------------------------------------------------------------- /public/src/reducer/loginState.js: -------------------------------------------------------------------------------- 1 | import {CHANGE_LOGIN_STATE} from '../action/index.js' 2 | 3 | export default function loginState(state=0,action){ 4 | switch (action.type){ 5 | case CHANGE_LOGIN_STATE: 6 | return action.userType; 7 | default: 8 | return state 9 | } 10 | } -------------------------------------------------------------------------------- /models/users.js: -------------------------------------------------------------------------------- 1 | var User=require("../mongo/mongo.js").User 2 | /*为用户模型添加方法*/ 3 | module.exports={ 4 | create:function(user){ 5 | return User.create(user).exec() 6 | }, 7 | getUserByName:function(name){ 8 | return User 9 | .findOne({name:name}) 10 | .addCreatedAt() 11 | .exec() 12 | } 13 | } -------------------------------------------------------------------------------- /public/src/route/talk.js: -------------------------------------------------------------------------------- 1 | // import Talk from '../component/talk/talk.js' 2 | 3 | const talk = { 4 | path: '/talk', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/talk/talk.js').default) 8 | }, 'Talk') 9 | } 10 | } 11 | 12 | module.exports = talk -------------------------------------------------------------------------------- /public/src/route/post.js: -------------------------------------------------------------------------------- 1 | // import Post from '../component/posts/post.js' 2 | 3 | const post = { 4 | path: '/post/:id', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/posts/post.js').default) 8 | }, 'Post') 9 | } 10 | } 11 | 12 | module.exports = post -------------------------------------------------------------------------------- /public/src/reducer/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux' 2 | import loginState from './loginState.js' 3 | import getData from './getData.js' 4 | import getMessages from './getMessages.js' 5 | 6 | const rootReducer=combineReducers({ 7 | loginState, 8 | getData, 9 | getMessages, 10 | }) 11 | 12 | export default rootReducer -------------------------------------------------------------------------------- /public/src/route/about.js: -------------------------------------------------------------------------------- 1 | // import About from '../component/about/about.js' 2 | 3 | const about = { 4 | path: '/about', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/about/about.js').default) 8 | }, 'About') 9 | } 10 | } 11 | 12 | module.exports = about -------------------------------------------------------------------------------- /public/src/route/posts.js: -------------------------------------------------------------------------------- 1 | // import Posts from '../component/posts/posts.js' 2 | 3 | const posts = { 4 | path: '/main', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/posts/posts.js').default) 8 | }, 'Posts') 9 | } 10 | } 11 | 12 | module.exports = posts -------------------------------------------------------------------------------- /public/src/route/write.js: -------------------------------------------------------------------------------- 1 | // import Write from '../component/posts/write.js' 2 | 3 | const write = { 4 | path: '/write', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/posts/write.js').default) 8 | }, 'Write') 9 | } 10 | } 11 | 12 | module.exports = write -------------------------------------------------------------------------------- /public/src/route/update.js: -------------------------------------------------------------------------------- 1 | // import Write from '../component/posts/write.js' 2 | 3 | const update = { 4 | path: '/updatePost/:id', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/posts/write.js').default) 8 | }, 'Update') 9 | } 10 | } 11 | 12 | module.exports = update -------------------------------------------------------------------------------- /public/src/js/ajaxReturn.js: -------------------------------------------------------------------------------- 1 | import Alert from 'react-s-alert' 2 | /*ajax请求成功之后的弹窗操作*/ 3 | export default function(res){ 4 | if(res.state===300){ 5 | Alert.warning(res.info,{ 6 | effect:"slide", 7 | timeout:2000 8 | }) 9 | }else if(res.state===400){ 10 | Alert.error(res.info,{ 11 | effect:"slide", 12 | timeout:2000 13 | }) 14 | } 15 | } -------------------------------------------------------------------------------- /public/src/route/signInput.js: -------------------------------------------------------------------------------- 1 | // import SignInput from '../component/sign/signInput.js' 2 | 3 | const signInput = { 4 | path: '/sign/:type', 5 | getComponent(nextState, cb) { 6 | require.ensure([], (require) => { 7 | cb(null, require('../component/sign/signInput.js').default) 8 | }, 'SignInput') 9 | } 10 | } 11 | 12 | module.exports = signInput -------------------------------------------------------------------------------- /public/src/reducer/getData.js: -------------------------------------------------------------------------------- 1 | import {GET_DATA,GET_DATA_SUCCESS,GET_DATA_ERROR} from '../action/index.js' 2 | 3 | export default function getData(state=[],action){ 4 | var newState=state; 5 | switch (action.type){ 6 | case GET_DATA: 7 | return newState; 8 | case GET_DATA_SUCCESS: 9 | return action.data; 10 | case GET_DATA_ERROR: 11 | return action.error 12 | default : 13 | return state 14 | } 15 | } -------------------------------------------------------------------------------- /public/src/js/getCookie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 从cookie字符串的键值对中获取指定的值 3 | * @param {string} cookies 源字符串 4 | * @param {string} key 键 5 | * @return {string} value 值 6 | */ 7 | function getCookie(cookies,key){ 8 | var value=""; 9 | if(cookies){ 10 | if(cookies.match(key)){ 11 | value=cookies.split(key+"=")[1].trim(); 12 | /*如果key后面还有其他键,则用;分割*/ 13 | if(value.match(";")){ 14 | value=value.split(";")[0].trim(); 15 | } 16 | } 17 | } 18 | return value 19 | } 20 | 21 | module.exports=getCookie -------------------------------------------------------------------------------- /models/messages.js: -------------------------------------------------------------------------------- 1 | var Message=require("../mongo/mongo.js").Message 2 | 3 | Message.plugin("sliceTo50",{ 4 | afterFind:function(messages){ 5 | var len=messages.length; 6 | return messages.slice(len-30,len) 7 | } 8 | }) 9 | 10 | module.exports={ 11 | create:function(message){ 12 | return Message 13 | .create(message) 14 | .exec() 15 | }, 16 | getPartMessages:function(){ 17 | return Message 18 | .find() 19 | .addCreatedAt() 20 | .sliceTo50() 21 | .exec() 22 | }, 23 | getAllMessages:function(){ 24 | return Message 25 | .find() 26 | .addCreatedAt() 27 | .exec() 28 | } 29 | } -------------------------------------------------------------------------------- /public/src/component/posts/write.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import InputArea from './inputArea.js' 3 | import {connect} from 'react-redux' 4 | import * as actions from '../../action/index.js' 5 | 6 | class Write extends React.Component{ 7 | constructor(props) { 8 | super(props); 9 | } 10 | componentDidMount() { 11 | if(this.props.params.id){ 12 | this.props.getData("posts/getRawPost/"+this.props.params.id) 13 | } 14 | } 15 | render(){ 16 | return ( 17 |
18 |

{this.props.params.id?"修改":"发表"}

19 | 20 |
21 | ) 22 | } 23 | } 24 | 25 | Write.PropTyoes={ 26 | data:PropTypes.array.isRequired, 27 | getData:PropTypes.func.isRequired 28 | } 29 | 30 | export default connect( 31 | (state)=>({data:state.getData}), 32 | actions 33 | )(Write) -------------------------------------------------------------------------------- /models/comments.js: -------------------------------------------------------------------------------- 1 | var Comment=require("../mongo/mongo.js").Comment 2 | 3 | module.exports={ 4 | create:function(comment){ 5 | return Comment 6 | .create(comment) 7 | .exec() 8 | }, 9 | /*通过用户名和评论id删除评论*/ 10 | delCommentByIdAndName:function (commentId,name){ 11 | return Comment 12 | .remove({name:name,_id:commentId}) 13 | .exec() 14 | }, 15 | /*通过评论id删除评论*/ 16 | delCommentById:function(commentId){ 17 | return Comment 18 | .remove({_id:commentId}) 19 | .exec() 20 | }, 21 | /*通过文章id删除所有评论*/ 22 | delCommentsByPostId:function(postId){ 23 | return Comment 24 | .remove({postId:postId}) 25 | .exec() 26 | }, 27 | /*通过文章id获取所有评论*/ 28 | getComments:function(postId){ 29 | return Comment 30 | .find({postId:postId}) 31 | .sort({_id:-1}) 32 | .addCreatedAt() 33 | .exec() 34 | }, 35 | /*获取评论数*/ 36 | getCommentsCount:function(postId){ 37 | return Comment 38 | .count({postId:postId}) 39 | .exec() 40 | } 41 | } -------------------------------------------------------------------------------- /public/src/route/root.js: -------------------------------------------------------------------------------- 1 | // import Posts from './posts.js' 2 | // import Post from './post.js' 3 | // import Write from './write.js' 4 | // import SignInput from './signInput.js' 5 | // import About from './about.js' 6 | // import Talk from './talk.js' 7 | 8 | const rootRoute = { 9 | path: '/', 10 | indexRoute: { 11 | getComponent(nextState, cb) { 12 | require.ensure([], (require) => { 13 | cb(null, require("../component/posts/posts.js").default) 14 | }, 'Posts') 15 | }, 16 | }, 17 | getComponent(nextState, cb) { 18 | require.ensure([], (require) => { 19 | cb(null, require("../component/nav.js").default) 20 | }, 'Nav') 21 | }, 22 | childRoutes: [ 23 | require("./posts.js"), 24 | require("./post.js"), 25 | require("./write.js"), 26 | require("./signInput.js"), 27 | require("./about.js"), 28 | require("./talk.js"), 29 | require("./update.js") 30 | ] 31 | } 32 | 33 | export default rootRoute -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require("path") 3 | module.exports = { 4 | entry: ['babel-polyfill', './public/src/index.js'], 5 | output: { 6 | path: path.join(__dirname, '/public/build'), 7 | filename: 'index.js', 8 | publicPath: "/build/", 9 | chunkFilename: '[name].[chunkhash:5].chunk.js', 10 | }, 11 | module: { 12 | loaders: [{ 13 | test: /\.js$/, 14 | loader: 'babel-loader?presets[]=es2015&presets[]=react' 15 | }, { 16 | test: /\.css$/, 17 | loader: 'style-loader!css-loader' 18 | }, { 19 | test: /\.(png|jpg)$/, 20 | loader: 'url-loader?limit=8192' 21 | }] 22 | }, 23 | externals: { 24 | 'react': 'React', 25 | 'react-dom': 'ReactDOM', 26 | 'redux': 'Redux', 27 | 'react-redux': 'ReactRedux', 28 | 'jquery': '$', 29 | 'socket': 'Socket', 30 | 'react-router': 'ReactRouter', 31 | }, 32 | plugins: [ 33 | new webpack.optimize.UglifyJsPlugin({ 34 | compress: { 35 | warnings: false, 36 | }, 37 | output: { 38 | comments: false, 39 | }, 40 | }), 41 | new webpack.DefinePlugin({ 42 | 'process.env': { 43 | 'NODE_ENV': JSON.stringify('production') 44 | } 45 | }) 46 | ] 47 | } -------------------------------------------------------------------------------- /public/src/component/posts/post.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import InputArea from './inputArea.js' 3 | import Article from './article.js' 4 | import {connect} from 'react-redux' 5 | import * as actions from '../../action/index.js' 6 | import QueueAnim from 'rc-queue-anim' 7 | var Loader = require('halogen/ClipLoader'); 8 | 9 | class Post extends React.Component{ 10 | constructor(props) { 11 | super(props); 12 | } 13 | componentDidMount() { 14 | this.props.getData("posts/getOnePost/"+this.props.params.id,{flash:true}) 15 | } 16 | render(){ 17 | if(this.props.data.length){ 18 | if(this.props.data[0].comments){ 19 | return ( 20 | 21 |
22 | 23 |
24 |
25 | ) 26 | }else{ 27 | return 28 | } 29 | 30 | }else{ 31 | return
32 | } 33 | } 34 | } 35 | 36 | Post.PropTypes={ 37 | data:PropTypes.array.isRequired, 38 | getData:PropTypes.func.isRequired 39 | } 40 | 41 | export default connect( 42 | (state)=>({data:state.getData}), 43 | actions 44 | )(Post) -------------------------------------------------------------------------------- /public/src/component/posts/posts.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import Article from './article.js' 3 | import $ from 'jquery' 4 | import {connect} from 'react-redux' 5 | import * as actions from '../../action/index.js' 6 | import QueueAnim from 'rc-queue-anim' 7 | var Loader = require('halogen/ClipLoader'); 8 | 9 | 10 | class Posts extends React.Component{ 11 | constructor(props) { 12 | super(props); 13 | } 14 | componentDidMount() { 15 | this.props.getData("posts/getAllPosts") 16 | } 17 | render(){ 18 | /*posts文章页的数据长度大于1时,才显示内容,是因为在单文章页切换回来时, 19 | 加载数据会因为网速原因造成Article组件的内容闪烁。 20 | 所以为了妥协,设置了一个有瑕疵的判断,这个判断会造成只有一篇文章时不显示。*/ 21 | if(this.props.Posts.length>1){ 22 | return ( 23 |
24 | 25 | {this.props.Posts.map(function(item,i){ 26 | return
27 | })} 28 | 29 |
30 | ) 31 | }else{ 32 | return 33 | } 34 | } 35 | } 36 | 37 | Posts.PropTypes={ 38 | Posts:PropTypes.array.isRequired, 39 | getData:PropTypes.func.isRequired 40 | } 41 | 42 | export default connect( 43 | state=>({Posts:state.getData}), 44 | actions 45 | )(Posts) 46 | 47 | -------------------------------------------------------------------------------- /mongo/mongo.js: -------------------------------------------------------------------------------- 1 | var Mongolass=require("mongolass") 2 | var moment=require("moment") 3 | var objectIdToTimestamp=require("objectid-to-timestamp") 4 | /*连接数据库*/ 5 | var mongolass=new Mongolass() 6 | mongolass.connect("mongodb://localhost:27017/tang") 7 | /*数据库插件,添加时间戳*/ 8 | mongolass.plugin("addCreatedAt",{ 9 | afterFind:function(results){ 10 | results.forEach(function(item){ 11 | item.created_at=moment(objectIdToTimestamp(item._id)).format("YYYY-MM-DD HH:mm"); 12 | }) 13 | return results 14 | }, 15 | afterFindOne:function(result){ 16 | if(result){ 17 | result.created_at=moment(objectIdToTimestamp(result._id)).format("YYYY-MM-DD HH:mm"); 18 | } 19 | return result 20 | } 21 | }) 22 | /*用户模型*/ 23 | exports.User=mongolass.model("User",{ 24 | name:{type:"string"}, 25 | password:{type:"string"}, 26 | }) 27 | exports.User.index({name:1},{unique:true}).exec() 28 | /*文章模型*/ 29 | exports.Post=mongolass.model("Post",{ 30 | title:{type:"string"}, 31 | content:{type:"string"}, 32 | good:[{type:"string"}], 33 | updateTime:{type:"string"}, 34 | pv:{type:"number"} 35 | }) 36 | /*评论模型*/ 37 | exports.Comment=mongolass.model("Comment",{ 38 | postId:{type:Mongolass.Types.ObjectId}, 39 | name:{type:"string"}, 40 | content:{type:"string"}, 41 | }) 42 | /*聊天信息模型*/ 43 | exports.Message=mongolass.model("Message",{ 44 | name:{type:"string"}, 45 | content:{type:"string"} 46 | }) 47 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 欢迎来到唐凯的博客 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/src/component/posts/comment.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import {Link} from 'react-router' 3 | import {connect} from 'react-redux' 4 | import * as actions from '../../action/index.js' 5 | import {hashHistory} from 'react-router' 6 | import $ from 'jquery' 7 | import ajaxReturn from '../../js/ajaxReturn.js' 8 | import Alert from 'react-s-alert' 9 | 10 | var getCookie=require("../../js/getCookie.js") 11 | 12 | class Comment extends React.Component{ 13 | constructor(props) { 14 | super(props); 15 | this.delComment=this.delComment.bind(this) 16 | } 17 | delComment(){ 18 | this.props.getData("comments/delComment/"+this.props.postId+"/"+this.props.data._id,{loginState:this.props.loginState},"post",true) 19 | } 20 | render(){ 21 | return ( 22 |
23 | {this.props.data.name}: {this.props.data.content} 24 | {((getCookie(document.cookie,"name")===this.props.data.name)&&(this.props.loginState===1))||(this.props.loginState===2)? 25 | :"" 26 | } 27 |
28 | ) 29 | } 30 | } 31 | 32 | Comment.PropTypes={ 33 | loginState:PropTypes.string.isRequired, 34 | data:PropTypes.object.isRequired, 35 | getData:PropTypes.func.isRequired 36 | } 37 | 38 | export default connect( 39 | (state)=>({loginState:state.loginState}), 40 | actions 41 | )(Comment) -------------------------------------------------------------------------------- /public/src/index.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | PropTypes 3 | } from 'react' 4 | import { 5 | render 6 | } from 'react-dom' 7 | import { 8 | Router, 9 | Route, 10 | hashHistory, 11 | IndexRoute 12 | } from 'react-router' 13 | // import Nav from './component/nav.js' 14 | // import Posts from './component/posts/posts.js' 15 | // import Post from './component/posts/post.js' 16 | // import Write from './component/posts/write.js' 17 | // import SignInput from './component/sign/signInput.js' 18 | import { 19 | Provider 20 | } from 'react-redux' 21 | import { 22 | createStore, 23 | applyMiddleware 24 | } from 'redux' 25 | import reducer from "./reducer/index.js" 26 | import thunk from 'redux-thunk' 27 | import PlaseWait from './component/pleaseWait.js' 28 | // import About from './component/about/about.js' 29 | // import Talk from './component/talk/talk.js' 30 | import routes from './route/root.js' 31 | 32 | const store = createStore(reducer, applyMiddleware(thunk)) 33 | render( 34 | 35 | 36 | {/* 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | */} 46 | 47 | , 48 | document.getElementById("main") 49 | ) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tang", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server --inline --content-base" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "babel-core": "^6.21.0", 14 | "babel-loader": "^6.2.10", 15 | "babel-polyfill": "^6.20.0", 16 | "babel-preset-es2015": "^6.18.0", 17 | "babel-preset-react": "^6.16.0", 18 | "body-parser": "^1.15.2", 19 | "compression": "^1.6.2", 20 | "cookie-parser": "^1.4.3", 21 | "css-loader": "^0.26.1", 22 | "express": "^4.14.0", 23 | "express-winston": "^2.1.0", 24 | "extract-text-webpack-plugin": "^1.0.1", 25 | "halogen": "^0.2.0", 26 | "jquery": "^3.1.1", 27 | "marked": "^0.3.6", 28 | "moment": "^2.17.1", 29 | "mongolass": "^2.4.1", 30 | "objectid-to-timestamp": "^1.3.0", 31 | "production": "0.0.2", 32 | "promise": "^7.1.1", 33 | "rc-queue-anim": "^0.13.0", 34 | "react": "^15.4.1", 35 | "react-dom": "^15.4.1", 36 | "react-redux": "^5.0.1", 37 | "react-router": "^3.0.0", 38 | "react-s-alert": "^1.2.2", 39 | "redux": "^3.6.0", 40 | "redux-thunk": "^2.1.0", 41 | "sha1": "^1.1.1", 42 | "socket.io": "^1.7.2", 43 | "style-loader": "^0.13.1", 44 | "webpack": "^1.14.0", 45 | "webpack-dev-server": "^1.16.2", 46 | "whatwg-fetch": "^2.0.1", 47 | "winston": "^2.3.0" 48 | }, 49 | "devDependencies": { 50 | "compression-webpack-plugin": "^0.3.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 项目简介 2 | 使用react技术栈搭建的个人博客,手机端显示为单页面应用。 3 | 4 | [git](https://github.com/tangkai123456/blog) 5 | 6 | ## 功能 7 | - 首页列表,加载所有的文章,预览文章的部分内容,同时显示赞、评论、浏览的数量。 8 | - 文章发布。 9 | - 文章页,能查看详细文章内容,点赞数以及评论。 10 | - 文章的点赞功能。 11 | - 用户的登录与注册。 12 | - 用户能进行评论并管理自己的评论。 13 | - 聊天 14 | 15 | ## 运用的技术主要有 16 | - 采用`react`技术栈,所有状态均由`redux`进行管理,通过`react-router`来设置页面路由。 17 | - 使用`express`+`mongolass`进行后台数据的管理与操作。 18 | - 前后端分离,使用`jquery`的ajax携带cookie进行数据交互。 19 | - 使用`react-s-alert`插件弹出提示消息。 20 | - 使用`babel`转译、`webpack`打包代码。 21 | - 使用`Ant Motion`动画框架实现页面切换动画。 22 | - 响应式设计,在pc端、pad端、手机端体验良好。 23 | - 使用`socket.io`完成聊一聊功能,聊天信息可以实时更新 24 | - `react`等库文件引用自[BootCDN](http://www.bootcdn.cn/)。 25 | - react-router结合webpack设置按需加载。 26 | 27 | ## 预览 28 | [博客](http://tangkai123456.xyz/) 29 | 30 | ## 运行项目 31 | ``` 32 | git clone https://github.com/tangkai123456/blog.git 33 | cd blog 34 | npm install 35 | node server 36 | ``` 37 | 38 | 39 | ## 状态树 40 | 本项目使用redux管理状态,状态树为: 41 | ``` 42 | state={ 43 | loginState,//存储登录状态,管理员登录时为2,普通用户登陆时为1,未登录为0 44 | getData:[//首页时存储所有文章信息,单文章页存储一篇文章的信息,但两种情况数据类型都为数组,但文章页时数组长度为1 45 | { 46 | ...postContent,//文章信息 47 | comments:[]//文章的评论,首页时没有这个属性 48 | }, 49 | ] 50 | } 51 | ``` 52 | 53 | ## redux 54 | 异步ajax使用了thunk中间件,thunk允许action的创建函数返回一个函数,满足条件的情况下才dispatch。 55 | 56 | 使用三个action进行标记,获取文章相关数据的actionCreator形式为: 57 | 1.发起请求时dispatch("GET_DATA") 58 | 2.请求成功并且获取数据时dispatch("GET_DATA_SUCCESS") 59 | 3.请求失败时dispatch("GET_DATA_ERROR") 60 | 61 | 项目中所有ajax数据请求都在action中,也只有ajax可以调用dispatch改变状态树,在组件中不直接调用dispatch,数据流清晰 62 | 63 | ## mongoDB数据结构 64 | 用户: 65 | ``` 66 | User={ 67 | name:{type:"string"}, 68 | password:{type:"string"}, 69 | } 70 | ``` 71 | 文章: 72 | ``` 73 | Post={ 74 | title:{type:"string"}, 75 | content:{type:"string"}, 76 | good:[{type:"string"}],//点赞数 77 | updateTime:{type:"string"}, 78 | pv:{type:"number"}//浏览数 79 | } 80 | ``` 81 | 评论: 82 | ``` 83 | Comment={ 84 | postId:{type:Mongolass.Types.ObjectId}, 85 | name:{type:"string"}, 86 | content:{type:"string"}, 87 | } 88 | ``` 89 | 90 | ## 待完善的功能 91 | - react-router过渡动画(已完成部分动画) 92 | - 加速首屏加载速度(已增加后台gzip压缩;所有库文件采用cdn加载) 93 | - `聊一聊`(已完成) 94 | - `关于`(暂时用个人简历代替) 95 | - bug记录本 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /routes/comments.js: -------------------------------------------------------------------------------- 1 | var express=require("express") 2 | var router=express.Router() 3 | var postModel=require("../models/posts.js") 4 | var commentModel=require("../models/comments.js") 5 | var getCookie=require("../public/src/js/getCookie.js") 6 | var Promise=require("promise") 7 | /*删除评论*/ 8 | router.post("/delComment/:postId/:commentId",function(req,res,next){ 9 | var commentId=req.params.commentId, 10 | postId=req.params.postId, 11 | name=getCookie(req.headers.cookie,"name"), 12 | loginState=req.body.loginState; 13 | if(!name&&!loginState){ 14 | return res.end(JSON.stringify({state:300,info:"请登录"})) 15 | } 16 | if(loginState==="2"){ 17 | Promise.all([ 18 | commentModel.delCommentById(commentId), 19 | postModel.getPostById(postId), 20 | commentModel.getComments(postId), 21 | ]) 22 | .then(function(result){ 23 | var post=result[1]; 24 | post.comments=result[2]; 25 | res.send(JSON.stringify({state:200,info:"删除成功",data:[post]})) 26 | }) 27 | .catch(function(e){ 28 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 29 | }) 30 | }else if(loginState==="1"){ 31 | Promise.all([ 32 | commentModel.delCommentByIdAndName(commentId,name), 33 | postModel.getPostById(postId), 34 | commentModel.getComments(postId), 35 | ]) 36 | .then(function(result){ 37 | var post=result[1]; 38 | post.comments=result[2]; 39 | res.send(JSON.stringify({state:200,info:"删除成功",data:[post]})) 40 | }) 41 | .catch(function(e){ 42 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 43 | }) 44 | } 45 | }) 46 | /*写评论*/ 47 | router.post("/writeComment/:postId",function(req,res,next){ 48 | var postId=req.params.postId, 49 | content=req.body.content, 50 | name=getCookie(req.headers.cookie,"name"), 51 | loginState=req.body.loginState; 52 | if(!name||!loginState){ 53 | return res.end(JSON.stringify({state:300,info:"请登录"})) 54 | } 55 | var comment={ 56 | name:name, 57 | content:content, 58 | postId:postId, 59 | } 60 | Promise.all([ 61 | commentModel.create(comment), 62 | postModel.getPostById(postId), 63 | commentModel.getComments(postId), 64 | ]) 65 | .then(function(result){ 66 | var post=result[1]; 67 | post.comments=result[2]; 68 | res.send(JSON.stringify({state:200,info:"评论成功",data:[post]})) 69 | }) 70 | .catch(function(e){ 71 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 72 | }) 73 | }) 74 | 75 | module.exports=router -------------------------------------------------------------------------------- /models/posts.js: -------------------------------------------------------------------------------- 1 | var Post=require("../mongo/mongo.js").Post 2 | var marked=require("marked") 3 | var Promise=require("promise") 4 | var commentModel=require("./comments.js") 5 | 6 | /*内容加载时marked转化*/ 7 | Post.plugin("contentToHtml",{ 8 | afterFind:function(posts){ 9 | return posts.map(function(post){ 10 | post.content=marked(post.content); 11 | return post 12 | }) 13 | }, 14 | afterFindOne:function(post){ 15 | if(post){ 16 | post.content=marked(post.content); 17 | } 18 | return post 19 | } 20 | }) 21 | /*截取首页加载文章的部分内容*/ 22 | Post.plugin("toPartOfConent",{ 23 | afterFind:function(posts){ 24 | return posts.map(function(post){ 25 | post.content=post.content.slice(0,400); 26 | return post 27 | }) 28 | } 29 | }) 30 | /*添加留言数*/ 31 | Post.plugin("addCommentsCount",{ 32 | afterFind:function(posts){ 33 | return Promise.all(posts.map(function(post){ 34 | return commentModel.getCommentsCount(post._id) 35 | .then(function(commentsCount){ 36 | post.commentsCount=commentsCount; 37 | return post 38 | }) 39 | })) 40 | }, 41 | afterFindOne:function(post){ 42 | if(post){ 43 | return commentModel.getCommentsCount(post._id) 44 | .then(function(count){ 45 | post.commentsCount=count; 46 | return post 47 | }) 48 | } 49 | return post 50 | } 51 | }) 52 | 53 | module.exports={ 54 | /*创建文章*/ 55 | create:function(post){ 56 | return Post 57 | .create(post) 58 | .exec(); 59 | }, 60 | /*通过文章id获取文章*/ 61 | getPostById:function(postId){ 62 | return Post 63 | .findOne({_id:postId}) 64 | .addCreatedAt() 65 | .addCommentsCount() 66 | .contentToHtml() 67 | .exec() 68 | }, 69 | /*获取所有文章*/ 70 | getAllPosts:function(){ 71 | return Post 72 | .find() 73 | .sort({_id:-1}) 74 | .addCreatedAt() 75 | .toPartOfConent() 76 | .addCommentsCount() 77 | .exec() 78 | }, 79 | /*更新文章*/ 80 | updatePostById:function(postId,data){ 81 | return Post 82 | .update({_id:postId},{$set:data}) 83 | .exec() 84 | }, 85 | /*删除文章*/ 86 | delPostById:function(postId){ 87 | return Post 88 | .remove({_id:postId}) 89 | .exec() 90 | /*删除文章同时删除评论*/ 91 | .then(function(res){ 92 | if(res.result.ok&&res.result.n>0){ 93 | return commentModel.delCommentsByPostId(postId) 94 | } 95 | }) 96 | }, 97 | /*通过文章id给pv加1*/ 98 | incPv:function incPv(postId){ 99 | return Post 100 | .update({_id:postId},{$inc:{pv:1}}) 101 | .exec() 102 | }, 103 | getRawPostById:function(postId){ 104 | return Post 105 | .findOne({_id:postId}) 106 | .exec() 107 | } 108 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require("express") 2 | var path = require("path") 3 | var routes = require("./routes/index.js") 4 | var bodyParser = require("body-parser") 5 | var cookieParser = require("cookie-parser") 6 | var compression = require("compression") 7 | var http = require("http").Server(app) 8 | var io = require("socket.io")(http) 9 | var messageModel = require("./models/messages.js") 10 | var Promise = require("promise") 11 | var getCookie = require("./public/src/js/getCookie.js") 12 | 13 | var app = express() 14 | app.use(compression()) 15 | /*静态路径*/ 16 | app.use(express.static(path.join(__dirname, "public"))) 17 | /*设置允许跨域*/ 18 | app.use(function(req, res, next) { 19 | res.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); 20 | res.setHeader("Access-Control-Allow-Credentials", "true"); 21 | next() 22 | }) 23 | /*解析cookie*/ 24 | app.use(cookieParser()) 25 | /*解析req中的body*/ 26 | app.use(bodyParser.json()); 27 | app.use(bodyParser.urlencoded({ 28 | extended: true 29 | })); 30 | 31 | routes(app); 32 | /*聊天室*/ 33 | io.listen("3001").on('connection', function(socket) { 34 | /*连接成功是发送一次数据*/ 35 | messageModel.getPartMessages() 36 | .then(function(result) { 37 | var messages = result; 38 | socket.emit('message', { 39 | state: 200, 40 | info: "获取成功", 41 | data: messages 42 | }); 43 | }) 44 | socket.on('message', function(msg) { 45 | if (msg.getAll) { 46 | messageModel.getAllMessages() 47 | .then(function(results) { 48 | socket.emit("message", { 49 | state: 200, 50 | info: "获取所有数据成功", 51 | data: results 52 | }) 53 | }) 54 | } else { 55 | if (!msg.name) { 56 | socket.on("message", { 57 | state: 300, 58 | info: "请登录" 59 | }) 60 | } 61 | Promise.all([ 62 | messageModel.create(msg), 63 | messageModel.getPartMessages() 64 | ]) 65 | .then(function(results) { 66 | var messages = results[1]; 67 | io.emit('message', { 68 | state: 200, 69 | info: "获取成功", 70 | data: messages, 71 | for: "everyone" 72 | }); 73 | }) 74 | } 75 | }); 76 | }); 77 | 78 | app.listen("80") 79 | console.log("open at 80") -------------------------------------------------------------------------------- /public/src/component/talk/talk.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | connect 4 | } from 'react-redux' 5 | import * as actions from '../../action/index.js' 6 | import QueueAnim from 'rc-queue-anim' 7 | import Alert from 'react-s-alert' 8 | import config from '../../../../config.js' 9 | var getCookie = require("../../js/getCookie.js") 10 | var Loader = require('halogen/ClipLoader'); 11 | 12 | class Talk extends React.Component { 13 | constructor(props) { 14 | super(props); 15 | this.sendMessage = this.sendMessage.bind(this) 16 | this.showAll = this.showAll.bind(this) 17 | } 18 | componentDidMount() { 19 | window.socket = io.connect(config.talkURL); 20 | socket.on("connect", function(msg) { 21 | console.log("connect success") 22 | }); 23 | socket.on("message", function(msg) { 24 | this.props.getMessages(msg) 25 | }.bind(this)) 26 | } 27 | componentDidUpdate(prevProps, prevState) { 28 | this.refs.messageBox.scrollTop = this.refs.messageBox.scrollHeight 29 | } 30 | sendMessage(e) { 31 | e.preventDefault(); 32 | var name = getCookie(document.cookie, "name"), 33 | content = this.refs.input.value; 34 | if (!name) { 35 | Alert.warning("请登录", { 36 | effect: "slide", 37 | timeout: 2000 38 | }) 39 | return 40 | } 41 | socket.send({ 42 | name: getCookie(document.cookie, "name"), 43 | content: this.refs.input.value 44 | }) 45 | this.refs.input.value = "" 46 | } 47 | showAll() { 48 | socket.send({ 49 | getAll: true 50 | }) 51 | } 52 | render() { 53 | var name = getCookie(document.cookie, "name") 54 | return ( 55 |
56 |
57 | 显示全部 58 | {this.props.data? 59 | ( 60 | this.props.data.map(function(item,i){ 61 | return ( 62 |
63 |
{item.name} {item.created_at}
64 |
{item.content}
65 |
66 | ) 67 | }.bind(this)) 68 | ):( 69 | 70 | )} 71 |
72 |
73 | 74 | 75 |
76 |
77 | ) 78 | } 79 | } 80 | 81 | export default connect( 82 | state => ({ 83 | loginState: state.loginState, 84 | data: state.getMessages 85 | }), 86 | actions 87 | )(Talk) -------------------------------------------------------------------------------- /public/src/component/posts/inputArea.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import $ from 'jquery' 3 | import {connect} from 'react-redux' 4 | import * as actions from '../../action/index.js' 5 | import {hashHistory} from 'react-router' 6 | import ajaxReturn from '../../js/ajaxReturn.js' 7 | import Alert from 'react-s-alert' 8 | import QueueAnim from 'rc-queue-anim' 9 | 10 | class InputArea extends React.Component{ 11 | constructor(props) { 12 | super(props); 13 | this.submit=this.submit.bind(this); 14 | } 15 | submit(e){ 16 | e.preventDefault(); 17 | var url=""; 18 | var data={}; 19 | if(this.props.defaultData){//修改文章 20 | data={content:this.refs.content.value,title:this.refs.title.value} 21 | this.props.getData("posts/updatePost/"+this.props.defaultData._id,data,"post",true) 22 | hashHistory.push("/") 23 | }else{//发表文章 24 | if(this.props.isPost){ 25 | data={content:this.refs.content.value,title:this.refs.title.value} 26 | this.props.getData("posts/writePost",data,"post",true) 27 | hashHistory.push("/") 28 | }else{//发表评论 29 | data={content:this.refs.content.value,loginState:this.props.loginState}; 30 | this.props.getData("comments/writeComment/"+this.props.postId,data,"post",true) 31 | if(this.props.loginState){ 32 | if(this.refs.title){ 33 | this.refs.title.value="" 34 | } 35 | this.refs.content.value="" 36 | } 37 | } 38 | } 39 | } 40 | componentDidUpdate() { 41 | /*从修改页跳到发表页,会有数据留下来,所以要清空一次*/ 42 | if(this.props.defaultData){ 43 | this.refs.title.value=this.props.defaultData?this.props.defaultData.title:""; 44 | this.refs.content.value=this.props.defaultData?this.props.defaultData.content:""; 45 | }else{ 46 | if(this.refs.title){ 47 | this.refs.title.value="" 48 | } 49 | this.refs.content.value="" 50 | } 51 | } 52 | render(){ 53 | return ( 54 |
55 | { 56 | this.props.isPost?(
57 | 58 | 59 |
):"" 60 | } 61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 | ) 69 | } 70 | } 71 | 72 | InputArea.PropTypes={ 73 | loginState:PropTypes.string.isRequired, 74 | isPost:PropTypes.bool.isRequired, 75 | defaultValue:PropTypes.object.isRequired, 76 | getData:PropTypes.func.isRequired 77 | } 78 | 79 | export default connect( 80 | (state)=>({loginState:state.loginState}), 81 | actions 82 | )(InputArea) 83 | -------------------------------------------------------------------------------- /public/src/component/nav.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import {Link} from 'react-router' 3 | import $ from 'jquery' 4 | import * as actions from '../action/index.js' 5 | import {connect} from 'react-redux' 6 | import Alert from 'react-s-alert' 7 | 8 | 9 | import 'react-s-alert/dist/s-alert-default.css'; 10 | import 'react-s-alert/dist/s-alert-css-effects/slide.css'; 11 | 12 | var getCookie=require("../js/getCookie.js") 13 | 14 | class Nav extends React.Component{ 15 | constructor(props) { 16 | super(props); 17 | this.signout=this.signout.bind(this) 18 | } 19 | signout(){ 20 | this.props.signActions("sign/out",{},true,true) 21 | } 22 | componentDidMount() { 23 | this.props.signActions("sign/flashIn",{name:getCookie(document.cookie,"name"),password:getCookie(document.cookie,"password")},true) 24 | } 25 | render(){ 26 | /*取出cookie中的name*/ 27 | let name=getCookie(document.cookie,"name"); 28 | return ( 29 |
30 | 61 | {this.props.loginState?( 62 |
63 | 欢迎 {name}  64 | 65 |
66 | ):( 67 |
68 | 69 | 70 |
71 | )} 72 |
73 |
74 | {this.props.children} 75 |
76 |
77 |
78 | 79 |
80 |
81 | ) 82 | } 83 | } 84 | 85 | Nav.PropTypes={ 86 | loginState:PropTypes.string.isRequired, 87 | signActions:PropTypes.func.isRequired 88 | } 89 | 90 | export default connect( 91 | state=>({loginState:state.loginState}), 92 | actions 93 | )(Nav) -------------------------------------------------------------------------------- /public/src/component/sign/signInput.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import $ from 'jquery' 3 | import {hashHistory} from 'react-router' 4 | import {connect} from 'react-redux' 5 | import * as actions from '../../action/index.js' 6 | import QueueAnim from 'rc-queue-anim' 7 | var getCookie=require("../../js/getCookie.js") 8 | /** 9 | * 登录与注册的表单,通过判断属性返回不同的结构和进行不同的js 10 | */ 11 | class SignInput extends React.Component{ 12 | constructor(props) { 13 | super(props); 14 | this.submit=this.submit.bind(this); 15 | this.checkPwd=this.checkPwd.bind(this) 16 | } 17 | submit(e){ 18 | /*提交表单,阻止默认事件,判断是注册还是登录拼接不一样的url和data*/ 19 | e.preventDefault(); 20 | var url="", 21 | data={ 22 | name:this.refs.username.value, 23 | password:this.refs.password.value, 24 | }; 25 | /*注册事件*/ 26 | if(this.props.params.type=="signup"){ 27 | 28 | url="sign/up"; 29 | /*登录事件*/ 30 | }else if(this.props.params.type=="signin"){ 31 | url="sign/in"; 32 | } 33 | if(url&&data){ 34 | this.props.signActions(url,data,false,true) 35 | } 36 | } 37 | checkPwd(){ 38 | if(this.props.params.type=="signup"){ 39 | var password=this.refs.password, 40 | rePassword=this.refs.rePassword, 41 | submit=this.refs.submit; 42 | if(password.value==rePassword.value){ 43 | submit.disabled=false 44 | rePassword.style.background="" 45 | }else{ 46 | rePassword.style.background="red" 47 | submit.disabled=true 48 | } 49 | }else{ 50 | this.refs.submit.disabled=false 51 | } 52 | } 53 | render(){ 54 | return ( 55 |
56 | 57 |
58 | 59 | 60 |
用户名为6-20个字符,不能使用空格
61 |
62 |
63 | 64 | 65 |
密码需要6-20个字符,不能使用空格
66 |
67 | {/*如果是注册,则显示确认密码*/} 68 | {this.props.params.type=="signup"?( 69 |
70 | 71 | 72 |
73 | ):""} 74 | 75 |
76 |
77 | ) 78 | } 79 | } 80 | 81 | SignInput.PropTypes={ 82 | loginState:PropTypes.string.isRequired, 83 | signActions:PropTypes.func.isRequired 84 | } 85 | 86 | export default connect( 87 | state=>({loginState:state.loginState}), 88 | actions 89 | )(SignInput) -------------------------------------------------------------------------------- /routes/sign.js: -------------------------------------------------------------------------------- 1 | var express=require("express") 2 | var router=express.Router() 3 | var sha1=require("sha1") 4 | var cookieParser=require("cookie-parser") 5 | 6 | var userModel=require("../models/users.js") 7 | /*使用cookie登录*/ 8 | router.post("/flashIn",function(req,res,next){ 9 | var name=req.body.name 10 | userModel.getUserByName(name) 11 | .then(function(result){ 12 | if(result){ 13 | /*如果用户存在,且密码与输入的密码相同,则成功*/ 14 | if(result.password===req.body.password){ 15 | var loginState=1; 16 | if(name==="tangkai"){ 17 | loginState=2 18 | } 19 | res.cookie("name",name,{maxAge:1000*60*60*24*10}); 20 | res.cookie("password",result.password,{maxAge:1000*60*60*24*10}); 21 | return res.send(JSON.stringify({state:200,info:"登录成功",loginState:loginState})) 22 | } 23 | } 24 | return res.send(JSON.stringify({state:300,info:"用户名或密码错误",loginState:0})) 25 | }) 26 | }) 27 | /*登录*/ 28 | router.post("/in",function(req,res,next){ 29 | var name=req.body.name 30 | userModel.getUserByName(name) 31 | .then(function(result){ 32 | if(result){ 33 | /*如果用户存在,且密码与输入的密码相同,则成功*/ 34 | if(result.password===sha1(req.body.password)){ 35 | res.cookie("name",name,{maxAge:1000*60*60*24*10}); 36 | res.cookie("password",result.password,{maxAge:1000*60*60*24*10}); 37 | var loginState=1; 38 | if(name==="tangkai"){ 39 | loginState=2 40 | } 41 | return res.send(JSON.stringify({state:200,info:"登录成功",loginState:loginState})) 42 | } 43 | } 44 | return res.send(JSON.stringify({state:300,info:"用户名或密码错误",loginState:0})) 45 | }) 46 | }) 47 | /*注册用户*/ 48 | router.post("/up",function(req,res,next){ 49 | var name=req.body.name, 50 | password=req.body.password; 51 | /*注册信息验证*/ 52 | if(name.match(" ")||name.match(" ")){ 53 | return res.end(JSON.stringify({state:300,info:"用户名或密码中存在空格"})) 54 | }else if(name.length<6||name.length>20||password.length<6||password.length>20){ 55 | return res.end(JSON.stringify({state:300,info:"用户名或密码长度不正确"})) 56 | } 57 | /*查询用户名是否重复*/ 58 | userModel.getUserByName(name) 59 | .then(function(result){ 60 | /*如果已经存在则返回repeat*/ 61 | if(result){ 62 | return res.end(JSON.stringify({state:300,info:"用户名已存在"})) 63 | }else{ 64 | var user={ 65 | name:req.body.name, 66 | password:sha1(req.body.password), 67 | } 68 | userModel.create(user) 69 | .then(function(result){ 70 | res.cookie("name",name,{maxAge:1000*60*60*24*10}); 71 | res.cookie("password",result.password,{maxAge:1000*60*60*24*10}); 72 | res.send(JSON.stringify({state:200,info:"登录成功",loginState:1})) 73 | }) 74 | .catch(function(e){ 75 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 76 | }) 77 | } 78 | }) 79 | .catch(function(e){ 80 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 81 | }) 82 | }) 83 | /*退出*/ 84 | router.post("/out",function(req,res,next){ 85 | res.clearCookie("name") 86 | res.clearCookie("password") 87 | res.send(JSON.stringify({state:200,info:"退出成功",loginState:0})) 88 | }) 89 | 90 | module.exports=router -------------------------------------------------------------------------------- /public/src/component/posts/article.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | import {Link} from 'react-router' 3 | import Comment from './comment.js' 4 | import InputArea from './inputArea.js' 5 | import {connect} from 'react-redux' 6 | import * as actions from '../../action/index.js' 7 | import $ from 'jquery' 8 | import {hashHistory} from 'react-router' 9 | import Alert from 'react-s-alert' 10 | import ajaxReturn from '../../js/ajaxReturn.js' 11 | import QueueAnim from 'rc-queue-anim' 12 | 13 | class Article extends React.Component{ 14 | constructor(props) { 15 | super(props); 16 | this.del=this.del.bind(this) 17 | this.clickGood=this.clickGood.bind(this) 18 | } 19 | del(){ 20 | this.props.getData("posts/deletePost/"+this.props.data._id,{},"post",true) 21 | hashHistory.push("/") 22 | } 23 | clickGood(){ 24 | this.props.getData("posts/clickGood/"+this.props.data._id+"/"+(this.props.data.comments?0:1),{},"post",true) 25 | } 26 | render(){ 27 | return ( 28 |
29 | 30 |

31 | {/*{this.props.data.title}*/} 32 |

33 | 34 |
发布时间:{this.props.data.created_at} 最后一次修改:{this.props.data.updateTime} 35 | {this.props.loginState==2?( 36 | 37 | 38 | ):"" 39 | } 40 |
41 | { 42 | this.props.data.comments?( 43 |
44 | 45 |
46 | 47 | {/*后台返回的是html字符串,需要用dangerouslySetInnerHTML放置到div中,防止script注入*/} 48 |
49 |
50 |
51 | ):( 52 | 53 |
54 | {/*后台返回的是html字符串,需要用dangerouslySetInnerHTML放置到div中,防止script注入*/} 55 |
56 | 57 | ) 58 | } 59 |
60 | {this.props.data.good.length}赞 {this.props.data.commentsCount}评论 {this.props.data.pv}浏览 61 |
62 | {this.props.children} 63 | 64 | {this.props.data.comments?this.props.data.comments.map(function(item,i){ 65 | return 66 | }.bind(this)):""} 67 | 68 |
69 | ) 70 | } 71 | } 72 | 73 | Article.PropTypes={ 74 | loginState:PropTypes.string.isRequired, 75 | data:PropTypes.object.isRequired, 76 | getData:PropTypes.func.isRequired 77 | } 78 | 79 | export default connect( 80 | (state)=>({loginState:state.loginState}), 81 | actions 82 | )(Article) -------------------------------------------------------------------------------- /routes/posts.js: -------------------------------------------------------------------------------- 1 | var express=require("express") 2 | var router=express.Router() 3 | var postModel=require("../models/posts.js") 4 | var commentModel=require("../models/comments.js") 5 | var getCookie=require("../public/src/js/getCookie.js") 6 | var moment=require("moment") 7 | var Promise=require("promise") 8 | /*获取所有文章*/ 9 | router.get("/getAllPosts",function(req,res,next){ 10 | postModel.getAllPosts() 11 | .then(function(posts){ 12 | res.send(JSON.stringify({state:200,info:"get all posts success",data:posts})) 13 | }) 14 | .catch(function(e){ 15 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 16 | }) 17 | }) 18 | /*写文章*/ 19 | router.post("/writePost",function(req,res){ 20 | if(getCookie(req.headers.cookie,"name")!=="tangkai"){ 21 | return res.send(JSON.stringify({state:300,info:"你没有权限"})) 22 | } 23 | var title=req.body.title; 24 | var content=req.body.content; 25 | var post={ 26 | title:title, 27 | content:content, 28 | good:[], 29 | updateTime:moment().format("YYYY-MM-DD HH:mm"), 30 | pv:0 31 | } 32 | postModel.create(post) 33 | .then(function(result){ 34 | res.send(JSON.stringify({state:200,info:"发表成功"})) 35 | }) 36 | .catch(function(){ 37 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 38 | }) 39 | }) 40 | /*获取一篇文章*/ 41 | router.get("/getOnePost/:postId",function(req,res,next){ 42 | var postId=req.params.postId; 43 | Promise.all([ 44 | postModel.getPostById(postId), 45 | commentModel.getComments(postId), 46 | req.query.flash?postModel.incPv(postId):null 47 | ]) 48 | .then(function(results){ 49 | var post=results[0]; 50 | post.comments=results[1]; 51 | res.send(JSON.stringify({state:200,info:"get post success",data:[post]})) 52 | }) 53 | .catch(function(){ 54 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 55 | }) 56 | }) 57 | /*获取一篇文章的初始内容,没有marked的*/ 58 | router.get("/getRawPost/:postId",function(req,res,next){ 59 | var postId=req.params.postId; 60 | postModel.getRawPostById(postId) 61 | .then(function(result){ 62 | res.send(JSON.stringify({state:200,info:"get post success",data:[result]})) 63 | }) 64 | .catch(function(){ 65 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 66 | }) 67 | }) 68 | /*删除一篇文章*/ 69 | router.post("/deletePost/:postId",function(req,res,next){ 70 | if(getCookie(req.headers.cookie,"name")!=="tangkai"){ 71 | return res.send(JSON.stringify({state:300,info:"你没有权限"})) 72 | } 73 | var postId=req.params.postId; 74 | Promise.all([ 75 | postModel.delPostById(postId), 76 | postModel.getAllPosts() 77 | ]) 78 | .then(function(results){ 79 | var posts=results[1]; 80 | res.send(JSON.stringify({state:200,info:"删除成功",data:posts})) 81 | }) 82 | .catch(function(){ 83 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 84 | }) 85 | }) 86 | /*修改文章*/ 87 | router.post("/updatePost/:postId",function(req,res,next){ 88 | if(getCookie(req.headers.cookie,"name")!=="tangkai"){ 89 | return res.send(JSON.stringify({state:300,info:"你没有权限"})) 90 | } 91 | var postId=req.params.postId, 92 | title=req.body.title, 93 | content=req.body.content; 94 | var post={ 95 | title:title, 96 | content:content, 97 | updateTime:moment().format("YYYY-MM-DD HH:mm") 98 | } 99 | postModel.updatePostById(postId,post) 100 | .then(function(){ 101 | res.end(JSON.stringify({state:200,info:"修改成功"})) 102 | }) 103 | .catch(function(){ 104 | res.send(JSON.stringify({state:400,info:"朋友,你的网络出现问题了"})) 105 | }) 106 | }) 107 | /*点赞,isMain参数表示是不是主页传来的请求,因为要根据不同的页面返回不同的数据*/ 108 | router.post("/clickGood/:postId/:isMain",function(req,res,next){ 109 | var name=getCookie(req.headers.cookie,"name"), 110 | postId=req.params.postId; 111 | if(!name){ 112 | return res.end(JSON.stringify({state:300,info:"请登录"})) 113 | } 114 | postModel.getRawPostById(postId) 115 | .then(function(result){ 116 | var isFound=false; 117 | for(var i=0,len=result.good.length;i { 22 | dispatch({ 23 | type: CHANGE_LOGIN_STATE, 24 | userType: 0 25 | }) 26 | // fetch("http://localhost/"+url,{ 27 | // method:"post", 28 | // body:JSON.stringify(data), 29 | // headers: { 30 | // 'Accept': 'application/json, text/javascript, */*; q=0.01', 31 | // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 32 | // }, 33 | // credentials: 'credentials', 34 | // }) 35 | // .then(function(response) { 36 | // return response.json() 37 | // }).then(function(json) { 38 | // console.log('parsed json', json) 39 | // }).catch(function(ex) { 40 | // console.log('parsing failed', ex) 41 | // }) 42 | $.ajax({ 43 | url: config.url + url, 44 | type: "post", 45 | data: data, 46 | dataType: "json", 47 | xhrFields: { 48 | withCredentials: true 49 | }, 50 | success: function(res) { 51 | if (res.loginState) { 52 | dispatch({ 53 | type: CHANGE_LOGIN_STATE, 54 | userType: res.loginState 55 | }) 56 | if (!noFlash) { 57 | hashHistory.push("/"); 58 | } 59 | } 60 | if (showAlert) { 61 | if (res.state === 200) { 62 | Alert.success(res.info, { 63 | effect: "slide", 64 | timeout: 2000 65 | }) 66 | } else if (res.state === 300) { 67 | dispatch({ 68 | type: "SHOW_INFO", 69 | info: { 70 | info: res.info, 71 | xuhao: 3 72 | } 73 | }) 74 | Alert.warning(res.info, { 75 | effect: "slide", 76 | timeout: 2000 77 | }) 78 | } else if (res.state === 400) { 79 | Alert.error(res.info, { 80 | effect: "slide", 81 | timeout: 2000 82 | }) 83 | } 84 | } 85 | }, 86 | error: function() { 87 | dispatch({ 88 | type: CHANGE_LOGIN_STATE, 89 | userType: 0 90 | }); 91 | Alert.error("朋友,你的网络出现问题了", { 92 | effect: "slide", 93 | timeout: 2000 94 | }) 95 | }, 96 | failed: function() { 97 | dispatch({ 98 | type: CHANGE_LOGIN_STATE, 99 | userType: 0 100 | }); 101 | Alert.error("朋友,你的网络出现问题了", { 102 | effect: "slide", 103 | timeout: 2000 104 | }) 105 | } 106 | }) 107 | } 108 | } 109 | 110 | /*ajax相关,需要有返回值的*/ 111 | export const GET_DATA = "GET_DATA" 112 | export const GET_DATA_SUCCESS = "GET_DATA_SUCCESS" 113 | export const GET_DATA_ERROR = "GET_DATA_ERROR" 114 | /** 115 | * 所有获取数据的ajax请求 116 | * @param {string} url 请求数据的后台接口 117 | * @param {object} data 传送到后台的数据 118 | * @param {string} type ajax种类 119 | * @param {bool} alert 是否弹出提示信息 120 | * @return {[type]} [description] 121 | */ 122 | export function getData(url, data, type = "get", alert) { 123 | return (dispatch, getState) => { 124 | dispatch({ 125 | type: GET_DATA, 126 | }) 127 | $.ajax({ 128 | url: config.url + url, 129 | type: type, 130 | data: data, 131 | xhrFields: { 132 | withCredentials: true 133 | }, 134 | dataType: "json", 135 | success: function(res) { 136 | if (res.data) { 137 | dispatch({ 138 | type: GET_DATA_SUCCESS, 139 | data: res.data 140 | }) 141 | } 142 | if (alert) { 143 | switch (res.state) { 144 | case 200: 145 | Alert.success(res.info, { 146 | effect: "slide", 147 | timeout: 2000 148 | }); 149 | break; 150 | case 300: 151 | Alert.warning(res.info, { 152 | effect: "slide", 153 | timeout: 2000 154 | }); 155 | break; 156 | case 400: 157 | Alert.error(res.info, { 158 | effect: "slide", 159 | timeout: 2000 160 | }) 161 | } 162 | } 163 | }.bind(this), 164 | error: function(res) { 165 | dispatch({ 166 | type: GET_DATA_ERROR, 167 | error: res 168 | }) 169 | Alert.error("朋友,你的网络出现问题了", { 170 | effect: "slide", 171 | timeout: 2000 172 | }) 173 | }, 174 | failed: function() { 175 | Alert.error("朋友,你的网络出现问题了", { 176 | effect: "slide", 177 | timeout: 2000 178 | }) 179 | } 180 | }) 181 | } 182 | } 183 | 184 | export const GET_MESSAGES = "GET_MESSAGES" 185 | 186 | export function getMessages(res) { 187 | return (dispatch, getState) => { 188 | if (res.state === 200) { 189 | dispatch({ 190 | type: GET_MESSAGES, 191 | data: res.data 192 | }) 193 | } else if (res.state === 300) { 194 | Alert.warning(res.info, { 195 | effect: "slide", 196 | timeout: 2000 197 | }) 198 | } else { 199 | Alert.error("朋友,你的网络出现问题了", { 200 | effect: "slide", 201 | timeout: 2000 202 | }) 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /public/css/index.css: -------------------------------------------------------------------------------- 1 | *{margin:0;padding:0;font-family: "MicroSoft YaHei";box-sizing:border-box;} 2 | html,body,#main{width:100%;height:100%;} 3 | .main{min-height:100%;background: url('../img/bg.jpg');background-attachment:fixed;} 4 | a{text-decoration: none;color:#654321} 5 | button{box-shadow: 1px 1px 5px #654321;margin:0 2px;border:none;transition:.2s;border-radius: 5px} 6 | button:hover{background:#654321;color:white;padding:10px!important;z-index:20;cursor: pointer} 7 | 8 | .loading-icon{position:absolute;left:50%;top:20%;margin-left:-25px} 9 | /* 左侧导航 */ 10 | nav{text-align:center;line-height:40px;font-size:18px;} 11 | nav ul{width:100%;list-style:none} 12 | nav li{transition: .5s} 13 | nav li:hover{color:black;} 14 | .logo{width:40px;height:40px;} 15 | .nav{height:100%;width:150px;background:rgba(101,67,33,.8);position:fixed;z-index:100;left:0;top:0;display:flex;justify-content:center} 16 | .nav-list{margin-top:50px;} 17 | .nav-list a{color:#fff} 18 | .nav-list a:nth-of-type(1){color:#654321;} 19 | .nav-list-bottom{position:absolute;width:150px;bottom:50px;left:0;text-align: center} 20 | .nav-list-bottom a{color:#fff} 21 | .sign-button-group{position:fixed;right:10px;top:2px;z-index:101} 22 | .sign-button-group button{border:none;padding:10px;} 23 | .alert{position: fixed;left:150px;top:0} 24 | .activeClass li{background:#8d7155;color:black;} 25 | 26 | @media screen and (max-width:768px){ 27 | .nav{position:fixed;bottom:0;top:auto;width:100%;height:3em} 28 | nav ul{display: flex;line-height:3em;justify-content:space-around} 29 | .nav-list>a:nth-of-type(1){position:fixed;top:0;left:0;width:100%;line-height:1;background:rgba(101,67,33,.8);text-align: left;} 30 | .nav-list{margin:0} 31 | .activeClass li{background:none;} 32 | .activeClass a:focus{background: none} 33 | .nav-list-bottom{position:static;width:auto} 34 | .nav-list-bottom{color:white} 35 | .nav-list span{visibility: hidden} 36 | .nav-posts li{background:url("../img/post n.png") no-repeat center;background-size: 50px 50px} 37 | .nav-talk li{background:url("../img/talk (1).png") no-repeat center;background-size: 25px 25px} 38 | .nav-about li{background:url("../img/about.png") no-repeat center;background-size: 36px 36px} 39 | .nav-list-bottom li{background:url("../img/write.png") no-repeat center;background-size: 25px 25px} 40 | .actPosts li{background:url("../img/icon post.png") no-repeat center;background-size: 35px 35px} 41 | .actTalk li{background:url("../img/talk.png") no-repeat center;background-size: 25px 25px} 42 | .actAbout li{background:url("../img/about (1).png") no-repeat center;background-size: 36px 36px} 43 | .actWrite li{background:url("../img/write (1).png") no-repeat center;background-size: 25px 25px} 44 | } 45 | /* 登录注册页面 */ 46 | .signInput{width:40%;margin:0 auto} 47 | .signInput input{width:100%;margin:5px 0;border-radius: 5px;border:none;padding:5px;transition:.2s} 48 | .signInput input:hover{box-shadow: 0 0 5px #654321;transform:scale(1.1)} 49 | .signInput input:focus{box-shadow: 0 0 5px #654321;transform:scale(1.1);border:none;outline:none} 50 | .input-check{font-size:12px;color:red} 51 | @media screen and (max-width:768px){ 52 | .signInput{width:70%} 53 | } 54 | /* 右侧内容区域 */ 55 | .main-right{padding-left:150px;padding-top:40px;} 56 | .main-right .container{width:70%;margin:0 auto;padding:15px 0;position: relative;} 57 | @media screen and (max-width:768px){ 58 | .main-right{padding-left:0} 59 | .main-right .container{width:95%} 60 | } 61 | 62 | .manager-btn{float:right;border:none;padding:4px 5px} 63 | /* 文章样式 */ 64 | .post{box-shadow: 0 0 10px lightgray;padding:15px;border-radius: 5px 5px;transition:.2s;margin-bottom:15px} 65 | .post .post-head{text-align: center;margin:10px 0 5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} 66 | .post .createTime{font-size:12px;line-height:24px} 67 | .createTime button{font-size:12px} 68 | .post .content{font-size:16px;margin:15px 0;} 69 | .post .comments-list{margin-bottom:5px;font-size:14px} 70 | .post .clickGood{cursor: pointer;} 71 | /* 文章内容样式 */ 72 | .content{padding:2px} 73 | .content h2{padding-bottom: 0.3em;font-size: 1.5em;border-bottom: 1px solid #654321;margin-bottom: 16px;font-weight: 600;line-height: 1.25;} 74 | .content p{margin-bottom: 16px;} 75 | .content ul{padding-left: 1em;margin-bottom: 16px;list-style:auto;} 76 | .content pre{padding:5px;overflow-x: auto;font-size: 85%;line-height: 1.45;background-color: rgba(255,255,255,.4);border-radius: 3px;margin-bottom:16px;} 77 | /* 主页时文章的样式 */ 78 | .posts-list .post-head{text-align:left;} 79 | .posts-list .post:hover{box-shadow: 0 5px 10px #654321;transform:translate(0,-5px)!important;background:rgba(255,255,255,.5)} 80 | .posts-list .content{height:64px;overflow: hidden;position:relative;}.posts-list .content::after {content: " ... "; bottom:0px;right: 3px;position: absolute;} 81 | /* 输入框 */ 82 | textarea{background:rgba(255,255,255,.5);width:100%;padding:10px;border:none;transition: .2s;resize:none} 83 | textarea:hover{box-shadow: 0 0 5px #654321;transform:scale(1.01);border-radius: 10px} 84 | textarea:focus{box-shadow: 0 0 5px #654321;transform:scale(1.01);border-radius: 10px;border:none;outline:none} 85 | .input-area button{font-size:14px;padding:3px 5px;} 86 | input{background:rgba(255,255,255,.5);} 87 | /* 评论 */ 88 | .aComment{position:relative;font-size:14px;margin:5px 0;padding-right:35px} 89 | .aComment .comment-user{color:#654321} 90 | .aComment .comment-content{color:gray} 91 | .aComment button{position: absolute;right:0;top:0;font-size:12px;padding:2px} 92 | /* 发表页面 */ 93 | .write-post h3{margin-bottom:5px;} 94 | .write-post #head{margin: 5px 0;width:100%;border-radius: 2px;border:1px solid lightgray;transition:.2s;padding:5px;} 95 | .write-post #head:hover{box-shadow: 0 0 5px #654321} 96 | .write-post textarea{resize:auto;height:200px} 97 | 98 | /* 敬请期待 */ 99 | .please-wait{text-align: center;font-size:20px} 100 | 101 | /* 聊天 */ 102 | .talkroom .message-box{position:fixed;width:60%;max-height: 70%;overflow-x: auto;padding:0 5px} 103 | .message-box .message{margin-bottom:15px;} 104 | .message-head{color:#666} 105 | .message-head .message-name{font-weight: bold} 106 | .message-head .message-time{font-size:12px;margin:0 5px} 107 | .talkroom .talk-input-group{position: fixed;top:80%;width:60%;z-index:10} 108 | .talk-input-group .talk-input{width:90%;margin:5px 0;border-radius: 5px;border:none;padding:5px;transition:.2s} 109 | .talk-input-group .talk-input:hover{box-shadow: 0 0 5px #654321} 110 | .talk-input-group .talk-input:focus{box-shadow: 0 0 5px #654321;border:none;outline:none} 111 | .self .message-name{float:right} 112 | .self .message-time{float:right} 113 | .self .message-content{float:right;} 114 | .self:after{content:" ";display:block;clear:both} 115 | .showAll{cursor:pointer} 116 | @media screen and (max-width:768px){ 117 | .talkroom .message-box{width:95%;padding:0 15px;height:90%} 118 | .talkroom .talk-input-group{width:100%} 119 | .talk-input-group .talk-input{width:80%} 120 | .talk-submit{background:#654321;color:white;padding:5px 10px!important;z-index:20;cursor: pointer} 121 | .talkroom .talk-input-group{top:auto;bottom:4em;} 122 | .talkroom .message-box{max-height:100%;padding-bottom:5.5em} 123 | } 124 | 125 | -------------------------------------------------------------------------------- /public/src/component/about/about.js: -------------------------------------------------------------------------------- 1 | import React,{PropTypes} from 'react' 2 | 3 | export default class About extends React.Component{ 4 | render(){ 5 | return ( 6 |
7 |
8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 | 唐凯 18 |
19 |
20 | hello world 21 |
22 |
23 |
24 | 25 | 26 | 计算机科学与技术 · 贵州大学 27 | 28 |
29 |
30 | 男   22岁   本科 31 |  1年工作经验1994.08年出生   北京 32 |
33 |
34 | 18519772682   35 | tangkai123456@live.com 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 工作经历 52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |

贵州鼎慧科技有限公司

62 | 前端 63 |
64 |
65 |
66 | 2016.03 -- 2016.10 67 |
68 |
69 | 70 |
71 |

1、根据产品设计的需求,配合后台开发人员实现产品的界面以及功能,维护及优化前端页面性能。

72 |

2、参与设计并且编写web前端构架以及应用。

73 |

3、产品、设计、后台开发各个部门沟通,利用HTML以及JavaScript等相关技术开发网站和客户端的前端页面。

74 |

4、利用css3以及JavaScript构建公司的网站,将公司的信息以及产品的介绍通过HTML的页面展示出来,并且保证产品信息的更新。

75 |
76 |
77 |
78 |
79 |
80 |
81 | 82 |
83 |
84 |
85 |
86 |
87 | 教育经历 88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 97 |
98 |
99 |

贵州大学

100 | 本科 · 计算机科学与技术 101 |
102 |
103 |
104 | 2016年毕业 105 |
106 |
107 |
108 |
109 |
110 |
111 | 112 |
113 |
114 |
115 |
116 |
117 | 项目经验 118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | 个人博客 127 |

全栈

128 |
129 |
130 | 131 |
132 | 2016.12 -- 2016.12 133 |
134 |
135 |
136 |

使用react+react-router构建前台页面,所有状态交由redux管理。

137 |

后台使用nodejs的express框架+mongodb进行数据操作与存储。

138 |

项目部署在腾讯云上

139 |
140 |
141 |
142 |
143 |
144 |
145 | 组件式开发全屏滚动网页 146 |

前端

147 |
148 |
149 | 150 |
151 | 2016.11 -- 2016.11 152 |
153 |
154 |
155 |

编写全屏滚动网页展示信息,分页使用fullPage库完成,链式调用组件加载页面,详细信息展示使用canvas实现。

156 |
157 |
158 |
159 |
160 |
161 |
162 | 个人简介网页(视差滚动) 163 |

前端

164 |
165 |
166 | 167 |
168 | 2016.11 -- 2016.11 169 |
170 |
171 |
172 |

使用skrollr库,制作视差滚动特效的个人简历网站

173 |
174 |
175 |
176 |
177 |
178 |
179 | 仿写糗事百科 180 |

前端

181 |
182 |
183 | 184 |
185 | 2016.11 -- 2016.11 186 |
187 |
188 |
189 |

使用webpack + react + react-router + nodejs + mongodb完成糗事百科部分功能

190 |
191 |
192 |
193 |
194 |
195 | 196 |
197 |
198 |
199 |
200 |
201 | 自我描述 202 |
203 |
204 |
205 |
206 |
207 | 208 | 209 | 唐凯 210 |
211 |
212 |

个人主页:www.tangkai123456.xyz

213 |

git: tangkai123456

214 |

熟练使用react、react-router、redux进行组件化开发,组件复用程度高。

215 |

熟悉es6,喜欢尝试新特性。

216 |

熟悉nodejs,常用express等框架编写后端逻辑。

217 |

能使用webpack、git对项目打包和版本控制。

218 |

熟练编写各种常用特效,以及ajax数据交互。

219 |

熟练使用bootstrap完成体验良好的响应式页面。

220 |

221 |

222 |

223 |

诚实开朗,思维活跃,喜欢学习新技术

224 |
225 |
226 |
227 |
228 | 229 |
230 |
231 |
232 |
233 |
234 | 期望工作 235 |
236 |
237 |
238 |
239 |
240 | 241 |
242 |
    243 |
  • 前端工程师
  • 244 |
  • 全职
  • 245 |
  • 北京
  • 246 |
  • 5k-10k
  • 247 |
248 |
249 |
250 |
251 |
252 |
253 | 254 |
255 |
256 |
257 |
258 |
259 | 技能评价 260 |
261 |
262 |
263 |
264 |
265 | html+css 266 | 267 | 268 | 269 | 精通 270 |
271 |
272 | js、jq 273 | 274 | 275 | 276 | 精通 277 |
278 |
279 | react、angular 280 | 281 | 282 | 283 | 掌握 284 |
285 |
286 | nodejs 287 | 288 | 289 | 290 | 掌握 291 |
292 |
293 | webpack、git 294 | 295 | 296 | 297 | 熟悉 298 |
299 |
300 |
301 |
302 |
303 | 304 |
305 |
306 | · 我目前已离职,可快速到岗 · 307 |
308 |
309 |
310 |
311 |
312 | ) 313 | } 314 | } -------------------------------------------------------------------------------- /public/build/About.6577d.chunk.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([5],{350:function(e,t,a){"use strict";function l(e){return e&&e.__esModule?e:{default:e}}function m(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function r(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){for(var a=0;a=0&&h.splice(r,1)}function o(t){var r=document.createElement("style");return r.type="text/css",m(t,r),r}function s(t){var r=document.createElement("link");return r.rel="stylesheet",m(t,r),r}function f(t,r){var a,i,e;if(r.singleton){var m=p++;a=d||(d=o(r)),i=l.bind(null,a,m,!1),e=l.bind(null,a,m,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(a=s(r),i=b.bind(null,a),e=function(){n(a),a.href&&URL.revokeObjectURL(a.href)}):(a=o(r),i=x.bind(null,a),e=function(){n(a)});return i(t),function(r){if(r){if(r.css===t.css&&r.media===t.media&&r.sourceMap===t.sourceMap)return;i(t=r)}else e()}}function l(t,r,a,i){var e=a?"":i.css;if(t.styleSheet)t.styleSheet.cssText=g(r,e);else{var m=document.createTextNode(e),n=t.childNodes;n[r]&&t.removeChild(n[r]),n.length?t.insertBefore(m,n[r]):t.appendChild(m)}}function x(t,r){var a=r.css,i=r.media;if(i&&t.setAttribute("media",i),t.styleSheet)t.styleSheet.cssText=a;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(a))}}function b(t,r){var a=r.css,i=r.sourceMap;i&&(a+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(i))))+" */");var e=new Blob([a],{type:"text/css"}),m=t.href;t.href=URL.createObjectURL(e),m&&URL.revokeObjectURL(m)}var c={},k=function(t){var r;return function(){return"undefined"==typeof r&&(r=t.apply(this,arguments)),r}},u=k(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),w=k(function(){return document.head||document.getElementsByTagName("head")[0]}),d=null,p=0,h=[];t.exports=function(t,r){r=r||{},"undefined"==typeof r.singleton&&(r.singleton=u()),"undefined"==typeof r.insertAt&&(r.insertAt="bottom");var a=e(t);return i(a,r),function(t){for(var m=[],n=0;n.dropdown_menu{margin-top:58px !important;*margin:70px 0 0 -220px !important;opacity:1;visibility:visible} 41 | 42 | .mr_w604{width:604px;margin:0 auto;} 43 | .mr_p_name{height:40px;line-height:38px;margin-bottom:8px;} 44 | .mr_name{display:block;height:40px;width:468px;margin:0 auto;line-height:40px;font-size:30px;color:#333;text-align:center;} 45 | .mr_intro{display:block;color:#333;text-align:center;margin:0 auto;} 46 | .mr_p_introduce .mr_intro{font-size:16px;width:468px;line-height:26px;} 47 | .mr_p_introduce .mr_intro_grey{color:#bdbdbd;font-style: italic;} 48 | .mr_edit{float:right;margin-right:12px;cursor:pointer;} 49 | .mr_edit em{font-size:16px;color:#00b38a;} 50 | .mr_edit i{display:inline-block;width:12px;height:14px;background:url(../images/myresume/icons_mr.png) -53px -6px no-repeat;margin-right:7px;} 51 | .mr_edit *{vertical-align: middle;} 52 | .mr_active{background-color: #fefef2;} 53 | /* 未创建在线 基本信息 */ 54 | .mr_myresume_l input{margin:0;} 55 | .mr_p_info{position:relative;color:#666;padding:5px 0 7px 0;} 56 | .mr_p_info .mr_edit{position:absolute;right:0px;top:4px;} 57 | .mr_edit i {display: inline-block;width: 12px;height: 14px;background: url(http://www.lagou.com/images/myresume/icons_mr.png) -53px -6px no-repeat;margin-right: 7px;} 58 | .mr_name_edit,.mr_intro_edit{padding-left:154px;margin-bottom:8px;} 59 | .mr_name_edit input,.mr_name_edit a,.mr_intro_edit input,.mr_introduce_edit *{vertical-align: middle;} 60 | .mr_name_edit .ed_name,.mr_intro_edit .ed_name{width:368px; height:30px;border:1px solid #eeeff1;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;margin:0;text-align:center;font: 30px/30px "微软雅黑","宋体",Arial;margin-right:12px;} 61 | .mr_name_edit input[type="text"]:focus,.mr_intro_edit input[type="text"]:focus{border-width:1px;} 62 | .mr_name_edit .save,.mr_intro_edit .save{border:none;margin:0;display:inline;padding:0 11px;*padding:0 4px;font-size:14px;height:30px;text-align:center;line-height:30px;color:#fff;background-color: #00b38a;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;margin-right:12px;} 63 | .mr_intro_edit .ed_name{font-size:16px;} 64 | .mr_name_edit .cancel,.mr_intro_edit .cancel{color:#00b38a;} 65 | .mr_p_info .info_t{text-align:center;margin-bottom:8px;} 66 | .mr_p_info .info_b{text-align:center;} 67 | .mr_p_info .mobile{margin-right:18px;} 68 | .mr_p_info .mobile i{width:12px;height:14px;background-position:-131px -70px;} 69 | .mr_p_info .email i{width:14px;height:10px;background-position:-150px -73px;} 70 | .mr_info_edit{padding:40px 0 50px 155px;background-color:#fefef2;} 71 | .mr_info_edit label{display:block;color:#afafa9;padding-bottom:4px;padding-left:3px;} 72 | .mr_info_on{padding-left:83px;} 73 | .form_wrap{position:relative;cursor:pointer;width:396px;height:46px;border:1px solid #f1f3e9;background-color:#fff;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;margin-bottom:9px;} 74 | .form_wrap .mr_button{text-align:left;padding-left:17px;width:327px;height:46px;background-color:#fff;} 75 | .form_wrap .mr_input{width:327px;padding:0 0 0 17px;border:none;height:46px;} 76 | .form_wrap .mr_input:focus{border:none;} 77 | .form_wrap .mr_selCity{width:395px;} 78 | .form_wrap_y .mr_button{width:150px;} 79 | .mr_sns_m .mr_button:focus{border:none;} 80 | .mr_sj{position:absolute;right:18px;top:20px;border: 6px solid #fff;border-color: #d3d3d3 transparent transparent;display: block;font-size: 0px;height: 0;width: 0;} 81 | .form_wrap_y .xl_list{width:190px;} 82 | .xl_list{position:absolute;top:49px;left:-1px;z-index:2;width:395px;background-color:#fff;border:1px solid #e7e7e7;box-shadow:2px 2px 4px #efefe4;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;} 83 | .xl_list li{height:36px;line-height:36px;padding-left:17px;} 84 | .xl_list li:hover{background-color:#f4f4f4;} 85 | .xl_list .mr_selCity{margin:0;} 86 | .xl_list *{list-style:none;} 87 | .form_wrap_y{width:188px;} 88 | .mr_basic .mr_basicform .mr_topdegree ul li,.mr_basic .mr_basicform .mr_workyear ul li,.mr_selCity ul.mr_province li,.mr_selCity ul.mr_province li,.mr_education ul li{ height: 33px;font-size: 14px;padding-left: 15px;line-height: 33px; cursor: pointer;} 89 | .mr_selCity ul.mr_province li{padding: 0;text-align: center;} 90 | .mr_selCity{height: 130px; background: #fff;width: 300px; overflow: hidden; margin-left: -1px;top: 45px; position: absolute;box-shadow:2px 2px 4px #efefe4;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #e7e7e7;} 91 | .mr_selCity{height: 130px; background: #fff;width: 300px; overflow: hidden; margin-left: -1px;top: 45px; position: absolute;box-shadow:2px 2px 4px #efefe4;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #e7e7e7;} 92 | .mr_selCity ul.mr_province{width: 90px; border-right: 1px solid #ececec;font-size: 14px;text-align: center;line-height: 33px; height: 130px; overflow: auto;} 93 | .mr_selCity ul.mr_province li span{ cursor: pointer; display: inline-block;width: 73px;} 94 | .mr_selCity ul.mr_province li ul{position: absolute;top:0;left: 90px;width: 209px;height: 130px;overflow: auto; color: #555} 95 | .mr_selCity ul.mr_province li ul li{float: left;padding: 0px 8px;border-radius: 3px;margin:4px;height: 23px; line-height: 23px; cursor: pointer;} 96 | .mr_locks{position:absolute;width:30px;height:30px;top:8px;right:8px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) -144px -83px no-repeat;} 97 | 98 | /*已创建在线简历 jason 2014/9/8*/ 99 | .mr_p_introduce{min-height:34px;line-height:32px;margin-bottom:4px;} 100 | .mr_add_m{position:absolute;top:44px;left:0;padding:18px 0 0 20px;background-color:#fff;z-index:2;border:1px solid #e7e7e7;box-shadow:2px 2px 4px #efefe4;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;} 101 | .mr_add_m li.mr0{margin-right:0;} 102 | .mr_month .mr0{margin-right:0;} 103 | .mr_year_se span i,.mr_year_se span em{vertical-align:middle;} 104 | .mr_year_se i{margin-right:11px;} 105 | .mr_years,.mr_man{margin-right:12px;} 106 | span.mr_man,span.mr_women{width:63px;padding-left:27px;color:#b5b5b5;;height:46px;line-height:46px;border:1px solid #f1f3e9;background-color:#fff;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;cursor:pointer;} 107 | span.active{color:#333;} 108 | span.mr_man i,span.mr_women i{display:inline-block;width:16px;height:16px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) no-repeat;} 109 | span.mr_man i{background-position:-4px -25px;} 110 | span.mr_man i.active{background-position:-4px -5px;} 111 | span.mr_women i{background-position:-27px -25px;} 112 | span.mr_women i.active{background-position:-27px -5px;} 113 | .sns_area .mr_sns_m{position:relative;width:534px;margin-bottom:16px;} 114 | .mr_add_sns{position:relative;} 115 | .mr_add_sns i,.mr_add_sns em{vertical-align:middle;} 116 | .mr_add_sns i{display:inline-block;margin-right:10px;width:15px;height:15px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) -5px -69px no-repeat;} 117 | .mr_add_sns em{color:#afafa9;} 118 | .mr_add_m i,.mr_add_m em{position:absolute;background:none;top:-16px;border: 8px solid #e7e7e7;border-color: transparent transparent #e7e7e7;display: block;font-size: 0px;height: 0;width: 0;} 119 | .mr_add_m em{border-color: transparent transparent #fff;top:-15px;} 120 | .mr_add_m ul{width:276px;} 121 | .mr_add_m li{float:left;width:42px;height:42px;margin:0 4px 10px 0;} 122 | .mr_add_m li.mr0{margin-right:0;} 123 | .mr_add_m .sns1{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -2px 3px no-repeat;} 124 | .mr_add_m .sns2{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -54px 3px no-repeat;} 125 | .mr_add_m .sns3{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -106px 3px no-repeat;} 126 | .mr_add_m .sns4{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -158px 3px no-repeat;} 127 | .mr_add_m .sns5{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -210px 3px no-repeat;} 128 | .mr_add_m .sns6{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -261px 3px no-repeat;} 129 | .mr_add_m .sns7{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -312px 3px no-repeat;} 130 | .mr_add_m .sns8{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -364px 3px no-repeat;} 131 | .mr_add_m .sns9{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -415px 3px no-repeat;} 132 | .mr_add_m .sns10{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -467px 3px no-repeat;} 133 | .mr_add_m .sns11{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -518px 3px no-repeat;} 134 | .mr_add_m .sns12{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -570px 3px no-repeat;} 135 | 136 | .mr_add_m .sns1.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -1px -43px no-repeat;} 137 | .mr_add_m .sns2.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -54px -43px no-repeat;} 138 | .mr_add_m .sns3.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -106px -43px no-repeat;} 139 | .mr_add_m .sns4.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -158px -43px no-repeat;} 140 | .mr_add_m .sns5.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -210px -43px no-repeat;} 141 | .mr_add_m .sns6.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -261px -43px no-repeat;} 142 | .mr_add_m .sns7.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -313px -43px no-repeat;} 143 | .mr_add_m .sns8.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -364px -43px no-repeat;} 144 | .mr_add_m .sns9.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -415px -43px no-repeat;} 145 | .mr_add_m .sns10.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -467px -43px no-repeat;} 146 | .mr_add_m .sns11.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -518px -43px no-repeat;} 147 | .mr_add_m .sns12.active{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -570px -43px no-repeat;} 148 | .mr_add_op{padding:10px 0;margin-left:-20px;background-color:#fafafa;} 149 | .mr_add_op a{vertical-align:middle;} 150 | .mr_add_op .mr_none_my{float:left;color:#b5b5b5;padding:6px 0;margin-left:20px;} 151 | .mr_add_op .sns_cancel,.mr_add_op .sns_save{float:right;padding:6px 13px;text-align:center;} 152 | .mr_add_op .sns_save{background-color:#00b88d;color:#fff;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;} 153 | .sns_add{cursor:pointer;} 154 | .mr_sns_m i{position:absolute;left:-43px;top:7px;width:30px;height:30px;background:url(http://www.lagou.com/images/myresume/sns_bg.png) no-repeat;} 155 | .mr_sns_m .mr_button{width:467px;padding:0 0 0 17px;border:none;} 156 | .mr_sns_m em{position:absolute;width:20px;height:20px;right:10px;top:13px;} 157 | .mr_sns_m .mr_ok{background:url(http://www.lagou.com/images/myresume/icons_mr.png) -4px -46px no-repeat;} 158 | #expectJob .mr_moudle_content{padding-bottom:25px;} 159 | /*在线简历页面主内容区域*/ 160 | .mr_myresume_l .mr_content{padding-top:26px;background-color: #fafafa;border: 1px solid #f2f2f2;border-bottom:2px solid #f2f2f2;border-top:none;border-bottom-right-radius: 3px;border-bottom-left-radius: 3px;} 161 | .mr_moudle_head{margin-bottom:23px;} 162 | #customBlock .mr_moudle_head{margin-bottom:33px;} 163 | .mr_moudle_head .mr_head_l{float:left;} 164 | .mr_moudle_head .mr_head_r{float:right;margin-top:3px;cursor:pointer;} 165 | .mr_title span{vertical-align:middle;display:inline-block;} 166 | .mr_title .mr_title_l{width:230px;height:0;border-top:1px solid #ededed;} 167 | .mr_title .mr_title_c{font-size:18px;padding:6px 24px;text-align:center;background-color:#eee;-moz-border-radius:26px;-webkit-border-radius:26px;border-radius:26px;margin:0 13px;} 168 | .mr_title .mr_title_r{width:152px;height:0;border-top:1px solid #ededed;} 169 | .mr_moudle_head .mr_head_r *{vertical-align:middle;} 170 | .mr_moudle_head .mr_head_r em{font-size:16px;color:#00b88d;} 171 | .mr_moudle_head .mr_head_r i{display: inline-block;width: 15px;height: 15px;background: url(http://www.lagou.com/images/myresume/icons_mr.png) -5px -69px no-repeat;margin-right:6px;} 172 | .mr_moudle_content{padding-bottom:21px;color:#555;font-size:14px;} /* 块 margin-bottom 48*/ 173 | .mr_moudle_content .mr_content_l{float:left;max-width: 445px;} 174 | .mr_moudle_content .mr_content_r{float:right;} 175 | .mr_content_l .l1{float:left;margin-right:10px;} 176 | .mr_content_l .l2{position: relative;float: left;padding-top: 10px;max-width: 380px;} 177 | #workExperience .mr_content_l .l2,#educationalBackground .mr_content_l .l2{padding-top:4px;} 178 | .mr_content_l .l2 a.projectTitle{/* position:relative; */font-size:16px;color:#555;text-decoration:none} 179 | .mr_content_l .l2 a.projectTitle:hover{color:#00b88d} 180 | .mr_content_l .l2 a.nourl{cursor:default;} 181 | .mr_content_l .l2 a.nourl:hover{color:#555;} 182 | .mr_content_l .l2 p{color:#999;} 183 | .mr_content_l .l2 a.projectTitle span{position:absolute;top:14px;right:-18px;width:12px;height:12px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) -95px -197px no-repeat;} 184 | .mr_content_l .l1 img{width:46px;height:46px;border:2px solid #eee;} 185 | .mr_content_l .l2 h4{font-size:16px;} 186 | .mr_content_l .l2 span{color:#999;display:block;} 187 | .mr_c_r_t{text-align:right;margin:5px 0 2px 0;*margin-top:8px;float:none;} 188 | .mr_content_r span{color:#999;} 189 | .mr_content_m{padding:14px 0 0 0px;*padding-left:1px;color:#555;} 190 | .mb46{margin-bottom:46px;} 191 | .mr_jobe_list{padding-bottom:44px;} 192 | /* #educationalBackground .mr_moudle_content{padding-bottom:15px;} */ 193 | .mr_wo_show{padding:13px 18px 46px 42px;background-color:#00b88d;margin-bottom:38px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;} 194 | .mr_wo_show p{line-height:26px;color:#fff;} 195 | .mr_self_site{color:#fff;margin-bottom:8px;font-size:24px;} 196 | .mr_self_sitelink {font-weight:bold;text-decoration:none;/* display:inline-block;width:558px; */word-break:break-all;color: #fff;} 197 | .mr_self_sitelink:hover{color:#fff;} 198 | .mr_wo_show .mr_c_r_t em{color:#50f9d2;} 199 | .mr_wo_show .mr_c_r_t i{background:url(http://www.lagou.com/images/myresume/icons_mr.png) -131px -115px no-repeat;} 200 | .wh43{width:100%;} 201 | .mr_work_upload .l2 span{display:inline-block;} 202 | .mr_wu_con .mr_work_title{margin:0 6px;} 203 | .mr_wu_show{margin-bottom:20px;} 204 | .mr_wu_show .mr_c_r_t{padding-top:4px;} 205 | .mr_wu_con .l2 span{color:#333;} 206 | .mr_wu_show .mr_wu_con_m{color:#555;} 207 | .mr_self_l{position:relative;height:81px;} 208 | #selfDescription .mr_head_r i,#expectJob .mr_head_r i,#skillsAssess .mr_head_r i,#customBlock .mr_head_r i{display: inline-block;width: 15px;height: 15px;background: url(http://www.lagou.com/images/myresume/icons_mr.png) -53px -6px no-repeat;margin-right: 7px;} 209 | /* .mr_self_l img{width:73px;height:73px;border-radius:50%;border:4px solid #f7f7f7;box-shadow:0px 2px 3px #dfdfdf;} */ 210 | .mr_self_l img{position:absolute;left:4px;width:73px;height:73px;top:8px;} 211 | .mr_self_l i{position:absolute;z-index:2;width:81px;height:81px;background:url(http://www.lagou.com/images/myresume/mr_txs.png) no-repeat;} 212 | .mr_moudle_content .mr_self_l{float:left;width:100px;padding-top:5px;} 213 | .mr_moudle_content .mr_self_r{float:left;width:504px;color:#555;line-height:26px;} 214 | #selfDescription .mr_moudle_content{padding-bottom:48px;} 215 | #expectJob .expectjob_list{position:relative;} 216 | #expectJob .mr_job_des i{position:absolute;width:32px;height:31px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) no-repeat;} 217 | #expectJob .mr_moudle_content .mr_job_t{left:0;top:1px;background-position:-160px -110px;} 218 | #expectJob .mr_moudle_content .mr_job_b{right:0;bottom:5px;background-position:-160px -153px;} 219 | .mr_moudle_content .mr_job_info{text-align:center;padding-left:28px;} 220 | .mr_job_info{color:#555;} 221 | .mr_job_info li{list-style:none;float:left;text-align:left;height:24px;line-height:24px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis;} 222 | .mr_name_li{width:188px;} 223 | .mr_jobtype_li{width:128px;} 224 | .mr_city_li{width:124px;} 225 | .mr_sns .sns10 span{min-width:56px;} 226 | .mr_jobrange_li{width:94px;} 227 | .mr_job_info i{display:inline-block;margin-right:10px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) no-repeat;} 228 | .mr_name_li i{width:9px;height:19px;background-position:-97px -220px;} 229 | .mr_jobtype_li i{width:17px;height:17px;background-position:-93px -247px;} 230 | .mr_city_li i{width:14px;height:17px;background-position:-93px -273px;} 231 | .mr_jobrange_li i{width:18px;height:18px;background-position:-92px -294px;} 232 | .mr_job_info li *{vertical-align:middle;} 233 | .mr_job_info li{overflow:hidden;white-space:nowrap;text-overflow: ellipsis;} 234 | 235 | .mr_job_info p .mr_333{color:#333;} 236 | .expectjob_list .mr_job_des {position:relative;margin-top:28px;padding:15px 38px 25px 50px;}.mr_moudle_content .mr_job_info{text-align:center;} 237 | .mr_job_info{color:#555;} 238 | .mr_job_info p .mr_333{color:#333;} 239 | .expectjob_list .clearfixs li{font-size:16px;} 240 | /* 241 | #skillsAssess{padding-bottom:50px;} 242 | .mr_skill_con{padding-left:20px;margin-bottom:18px;} 243 | .mr_skill_con span{vertical-align:middle;display:inline-block;} 244 | .mr_skill_con .mr_skill_name{width:90px;height:22px;text-align:right;margin-right:13px;word-wrap:break-word;white-space:nowrap;overflow:hidden;text-overflow: ellipsis;} 245 | .mr_skill_con .mr_skill_name:after{ 246 | content:"..."; 247 | } 248 | .mr_skill_con .mr_skill_plan{position:relative;width:420px;height:8px;border-radius:4px;background-color:#eee;margin-right:21px;overflow:hidden;} 249 | .mr_skill_plan em{position:absolute;left:0;top:0;display:inline-block;height:8px;width:370px;background-color:#00b88d;border-radius:4px;overflow:hidden;} */ 250 | #skillsAssess{padding-bottom:12px;} 251 | .mr_skill_con{padding-left:16px;margin-bottom:14px;position:relative;} 252 | .mr_skill_con span{vertical-align:middle;display:inline-block;} 253 | .mr_skill_con .mr_skill_name{width:90px;height:22px;text-align:right;margin-right:13px;word-wrap:break-word;white-space:nowrap;overflow:hidden;text-overflow: ellipsis;} 254 | /* .mr_skill_con .mr_skill_name:after{ 255 | content:"..."; 256 | } */ 257 | .mr_skill_con .mr_skill_plan{position:relative;width:410px;height:8px;border-radius:4px;background-color:#eee;margin-right:21px;overflow:hidden;} 258 | .mr_skill_plan em{position:absolute;left:0;top:0;display:inline-block;height:8px;background-color:#00b88d;border-radius:4px;overflow:hidden;} 259 | .mr_skill_con .mr_skill_delete{width:13px;height:13px;background:url(http://www.lagou.com/images/myresume/skill_delete.png) center top no-repeat;cursor:pointer;} 260 | .mr_moudle_content .mr_skill_add{padding-left:125px;*padding-left:104px;color:#999999;font-size:16px;} 261 | .mr_skill_add span{display:inline-block;width:88px;text-indent:22px;background:url(http://www.lagou.com/images/myresume/skill_add.png) left 3px no-repeat;cursor:pointer;} 262 | #customBlock{padding-bottom:30px;} 263 | #customBlock .mr_title{position:relative;} 264 | .mr_title .cust_title{position:absolute;top:-4px;left: 50%;padding:0 13px;background-color:#FAFAFA;} 265 | #customBlock #width604{width:604px;} 266 | #customBlock .mr_line_tl{width:528px;height: 0;border-top: 1px solid #ededed;} 267 | .cust_title span{padding:6px 24px;text-align: center;font-size: 18px;background-color: #eee;-moz-border-radius: 26px;-webkit-border-radius: 26px;border-radius: 26px;} 268 | #customBlock .mr_prolink .mr_btn{width:484px;} 269 | .mr_skill_con .mr_skill_circle{display:inline-block;width:20px;height:19px;position:absolute;left:122px;top:3px;background:url(http://www.lagou.com/images/myresume/skill_circle.png) no-repeat center top;} 270 | .mr_skill_circle em{display:inline-block;width:29px;height:23px;position:absolute;right: -4px;top: -22px;text-align: center;line-height:18px;padding: 0px;color: #ffffff;background:url(http://www.lagou.com/images/myresume/skill_img.png) center top no-repeat;font-size:10px;} 271 | 272 | 273 | /****一句话介绍****/ 274 | .mrcenter{margin:0 auto 20px auto; float:none;} 275 | 276 | /*创建在线简历 基本信息样式*/ 277 | .mr_infoed{margin-bottom:63px;} 278 | .mr_created .mr_infoed{margin-bottom:0px;} 279 | .mr_p_info .shenfen{margin-right:10px;} 280 | .mr_p_info .shenfen i{width:14px;height:12px;background-position:-90px -72px;} 281 | .mr_p_info .base_info{} 282 | .mr_p_info .base_info i{width:13px;height:13px;background-position:-111px -70px;} 283 | .mr_p_info .base_info em{margin-right:12px;} 284 | .mr_p_info .base_info em.mr0{margin:0;} 285 | #workExperience .mr_content_m{padding:4px 0 1px 0;} 286 | #workExperience .list_show{padding-bottom:6px;} 287 | #workExperience .mr_moudle_content{padding-bottom:0;} 288 | #educationalBackground .mb46{margin-bottom:0;} 289 | #educationalBackground .mr_moudle_content{padding-bottom:5px;} 290 | #projectExperience .mr_content_m{padding-top:8px;} 291 | #projectExperience .mr_jobe_list{padding-bottom:32px;} 292 | #projectExperience .mr_moudle_content{padding-bottom:18px;} 293 | .mr_sns a{position:relative;display:inline-block;width:30px;background:none;height:30px;margin:0 8px;} 294 | .mr_sns a span{position:absolute;min-width:28px;display:none;top:-29px;left:50%;height:20px;line-height:20px;line-height:18px\9;color:#fff;text-align:center;background-color:#666660;padding:0 10px;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;} 295 | .mr_sns a em{position:absolute; top:20px;left:50%;margin-left:-4px;font-size:0px; height:0; width:0; border-width:4px 4px 0; border-style:solid dashed; border-color:#666 transparent transparent;overflow: hidden;-webkit-transition:all 0.4s ease 0s;-moz-transition:all 0.4s ease 0s;-o-transition:all 0.4s ease 0s;transition:all 0.4s ease 0s;} 296 | .mr_sns .mr0{margin-right:0;} 297 | .mr_sns i{display:inline-block;width:30px;background:none;height:30px;margin-right:17px;} 298 | .mr_sns .sns1,.mr_sns_m .sns1,.mr_add_m .sns1{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -4px -5px no-repeat;} 299 | .mr_sns .sns2,.mr_sns_m .sns2,.mr_add_m .sns2{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -56px -5px no-repeat;} 300 | .mr_sns .sns3,.mr_sns_m .sns3,.mr_add_m .sns3{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -108px -5px no-repeat;} 301 | .mr_sns .sns4,.mr_sns_m .sns4,.mr_add_m .sns4{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -160px -5px no-repeat;} 302 | .mr_sns .sns5,.mr_sns_m .sns5,.mr_add_m .sns5{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -212px -5px no-repeat;} 303 | .mr_sns .sns6,.mr_sns_m .sns6,.mr_add_m .sns6{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -263px -5px no-repeat;} 304 | .mr_sns .sns7,.mr_sns_m .sns7,.mr_add_m .sns7{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -314px -5px no-repeat;} 305 | .mr_sns .sns8,.mr_sns_m .sns8,.mr_add_m .sns8{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -366px -5px no-repeat;} 306 | .mr_sns .sns9,.mr_sns_m .sns9,.mr_add_m .sns9{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -417px -5px no-repeat;} 307 | .mr_sns .sns10,.mr_sns_m .sns10,.mr_add_m .sns10{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -469px -5px no-repeat;} 308 | .mr_sns .sns11,.mr_sns_m .sns11,.mr_add_m .sns11{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -520px -5px no-repeat;} 309 | .mr_sns .sns12,.mr_sns_m .sns12,.mr_add_m .sns12{background:url(http://www.lagou.com/images/myresume/sns_bg.png) -572px -5px no-repeat;} 310 | .mr_infoed .mr_sns{text-align:center;padding-top:20px;} 311 | #projectExperience .mr_content_l .l2{padding-top:4px;} 312 | .mr_ope .mr_delete{position:relative;cursor:pointer;float:right;font-size:16px;color:#ff685e;margin-top:13px;} 313 | .mr_ope{padding-top:34px;} 314 | .mr_ope .mr_save,.mr_ope .mr_cancel{padding:12px 18px;font-size:16px;vertical-align:middle;} 315 | .mr_cancel{color:#00b88d;} 316 | .mr_save{background-color:#00b38a;margin-right:5px;color:#fff;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;} 317 | 318 | /*底部求职者职场状态*/ 319 | .mr_self_state{position:relative;height:94px;background-color:#f6f6f6;color:#555;font-size:14px;text-align: center;} 320 | .mr_self_state *{list-style:none;} 321 | .mr_self_state .form_wrap{position:absolute;top:50%;left:50%;margin:-24px 0 0 -135px;border:1px solid #eaeced;width:270px;} 322 | .mr_self_state .mr_button{width:240px;} 323 | .mr_self_state .select_color{border-color:#32dbb3;} 324 | .mr_self_state .xl_list{width:270px;} 325 | .mr_bottom{margin-top:5px;} 326 | .mr_bottom *{list-style:none;} 327 | .mr_bottom .mr_bottom_l{float:left;position:relative;cursor:pointer;} 328 | .mr_bottom .mr_bottom_r{float:right;} 329 | .mr_bottom_l i{display:inline-block;margin-right:10px;width:16px;height:12px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) -90px -140px no-repeat;} 330 | .mr_bottom_l em{color:#c5c5c5;} 331 | .mr_bottom .mr_down:hover em{color:#333;} 332 | .mr_bottom .mr_down:hover i{background:url(http://www.lagou.com/images/myresume/icons_mr.png) -117px -140px no-repeat;} 333 | .mr_bottom_r{text-align:right;color:#c6c6c6;} 334 | .mr_bottom_l .mr_down_tip{position:absolute; width:96px;height:116px;top:-126px;left:12px;background-color:#fff;border:1px solid #e7e7e7;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;} 335 | .mr_down_tip i, .mr_down_tip em {position: absolute;background: none;border: 8px solid #e7e7e7;border-color:#e7e7e7 transparent transparent;display: block;font-size: 0px;height: 0;width: 0;} 336 | .mr_down_tip i{right:2px;bottom:-16px;} 337 | .mr_down_tip em{right:12px;bottom:-15px;border-color:#fff transparent transparent;;} 338 | .mr_down_tip li{height:38px;line-height:38px;} 339 | .mr_down_tip li a{display:block;text-align:center;} 340 | .mr_down_tip li.active a{background-color:#00b88d;color:#fff;} 341 | .mr_sns_m .mr_no{background:url(http://www.lagou.com/images/myresume/icons_mr.png) -28px -46px no-repeat;} 342 | .mr_sns_m .sns_del{position:absolute;width:16px;height:16px;right:-30px;top:15px;background:url(http://www.lagou.com/images/myresume/icons_mr.png) -57px -49px no-repeat;} 343 | .mr_add_sns{position:relative;} 344 | .m_portrait{width:100%;height:135px;position:relative;z-index:2;background:url(http://www.lagou.com/images/myresume/head_bg.jpg) no-repeat;} 345 | .m_portrait .opa{position:absolute;} 346 | .mr_wo_preview ol,.mr_wu_con_m ol,.mr_self_r ol,.mr_expjob_content ol,.mr_wo_preview ul,.mr_wu_con_m ul,.mr_self_r ul,.mr_expjob_content ul{padding-left:19px;} 347 | .olpf ol,.olpf ul{padding-left:19px;} 348 | .mr_wo_preview {color:#fff;} 349 | .mr_preview .mr_title .mr_title_r{width:228px;} 350 | .resume_status {background: #f6f6f6;border: none!important;text-align: center;margin: -12px 0 0 -135px!important;cursor: default;} 351 | .m_portrait div { 352 | /* background: url(http://www.lagou.com/images/profile_cover.png) no-repeat; 353 | width: 120px; 354 | height: 120px; 355 | position: absolute; 356 | z-index: 5; */ 357 | width:200px; 358 | height:132px; 359 | position:absolute; 360 | left:41%; 361 | top:39px; 362 | z-index:100; 363 | } -------------------------------------------------------------------------------- /public/build/SignInput.d5958.chunk.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([4],{329:function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(330),a=n(o);t.default=a.default,e.exports=t.default},330:function(e,t,r){(function(n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function a(e,t){for(var r=Object.getOwnPropertyNames(t),n=0;n=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function l(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==("undefined"==typeof t?"undefined":c(t))&&"function"!=typeof t?e:t}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+("undefined"==typeof t?"undefined":c(t)));e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):a(e,t))}var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};Object.defineProperty(t,"__esModule",{value:!0});var p=Object.assign||function(e){for(var t=1;t=1?t:1,a=(r||1)/(t<1?t:1),i=a/Math.PI*2*(Math.asin(1/o)||0);return-(o*Math.pow(2,10*(n-=1))*Math.sin((n-i)*a))},easeOutElastic:function(e,t,r){var n=t>=1?t:1,o=(r||1)/(t<1?t:1),a=o/Math.PI*2*(Math.asin(1/n)||0);return n*Math.pow(2,-10*e)*Math.sin((e-a)*o)+1},easeInOutElastic:function(e,t,r){var n=e,o=t>=1?t:1,a=(r||1)/(t<1?t:1),i=a/Math.PI*2*(Math.asin(1/o)||0);return n*=2,n<1?-.5*(o*Math.pow(2,10*(n-=1))*Math.sin((n-i)*a)):o*Math.pow(2,-10*(n-=1))*Math.sin((n-i)*a)*.5+1},easeInBounce:function(e){var t=e,r=1-t;return r<1/2.75?1-7.5625*t*t:t<2/2.75?1-(7.5625*(t-=1.5/2.75)*t+.75):t<2.5/2.75?1-(7.5625*(t-=2.25/2.75)*t+.9375):1-(7.5625*(t-=2.625/2.75)*t+.984375)},easeOutBounce:function(e){var t=e;return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(e){var t=e,r=t<.5;return t=r?1-2*t:2*t-1,t<1/2.75?t*=7.5625*t:t=t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375,r?.5*(1-t):.5*t+.5}},b=void 0;"undefined"!=typeof document&&"undefined"!=typeof window?(b=r(335),Object.keys(v).forEach(function(e){b.Easings&&(b.Easings[e]=v[e])})):b=function(){var e=arguments[arguments.length-1];n(function(){return e()})};var w={easeInBack:[.6,-.28,.735,.045],easeOutBack:[.175,.885,.32,1.275],easeInOutBack:[.68,-.55,.265,1.55]},x="ant-queue-anim-placeholder-",P=function(){},S=function(e){function t(){s(this,t);var r=l(this,e.apply(this,arguments));k.call(r),r.keysToEnter=[],r.keysToLeave=[],r.keysAnimating=[],r.placeholderTimeoutIds={};var n=(0,m.toArrayChildren)((0,m.getChildrenFromProps)(r.props)),o={};return n.forEach(function(e){e&&e.key&&(r.props.appear?r.keysToEnter.push(e.key):o[e.key]=!0)}),r.originalChildren=(0,m.toArrayChildren)((0,m.getChildrenFromProps)(r.props)),r.state={children:n,childrenShow:o},r}return u(t,e),t.prototype.componentDidMount=function(){this.props.appear&&this.componentDidUpdate()},t.prototype.componentWillReceiveProps=function(e){var t=this,r=(0,m.toArrayChildren)(e.children),n=this.originalChildren,o=(0,m.mergeChildren)(n,r),a=o.length?this.state.childrenShow:{};this.keysToLeave.forEach(function(r){var n=(0,y.findDOMNode)(t.refs[r]);b(n,"stop"),e.enterForcedRePlay&&delete a[r]}),this.keysToEnter=[],this.keysToLeave=[],this.keysAnimating=[],this.setState({childrenShow:a,children:o}),r.forEach(function(e){if(e){var r=e.key,o=(0,m.findChildInChildrenByKey)(n,r);!o&&r&&t.keysToEnter.push(r)}}),n.forEach(function(e){if(e){var n=e.key,o=(0,m.findChildInChildrenByKey)(r,n);!o&&n&&t.keysToLeave.push(n)}})},t.prototype.componentDidUpdate=function(){this.originalChildren=(0,m.toArrayChildren)((0,m.getChildrenFromProps)(this.props));var e=Array.prototype.slice.call(this.keysToEnter),t=Array.prototype.slice.call(this.keysToLeave);0===this.keysAnimating.length&&(this.keysAnimating=e.concat(t)),e.forEach(this.performEnter),t.forEach(this.performLeave)},t.prototype.componentWillUnmount=function(){var e=this;[].concat(this.keysToEnter,this.keysToLeave,this.keysAnimating).forEach(function(t){return e.refs[t]&&b((0,y.findDOMNode)(e.refs[t]),"stop")}),Object.keys(this.placeholderTimeoutIds).forEach(function(t){clearTimeout(e.placeholderTimeoutIds[t])}),this.keysToEnter=[],this.keysToLeave=[],this.keysAnimating=[]},t.prototype.render=function(){var e=this,t=(0,m.toArrayChildren)(this.state.children).map(function(t){return t&&t.key?e.state.childrenShow[t.key]?(0,f.cloneElement)(t,{ref:t.key,key:t.key}):(0,f.createElement)("div",{ref:x+t.key,key:x+t.key}):t}),r=i(this.props,[]);return["component","interval","duration","delay","type","animConfig","ease","leaveReverse","animatingClassName","enterForcedRePlay","onEnd","appear"].forEach(function(e){return delete r[e]}),(0,f.createElement)(this.props.component,p({},r),t)},t}(d.default.Component),k=function(){var e=this;this.getVelocityConfig=function(t){for(var r=arguments.length,n=Array(r>1?r-1:0),o=1;o=0){s="transform";var l=i[(0,m.checkStyleName)(s)];if(l&&"none"!==l&&l.match(t)){var u=new RegExp("^.*"+t+"\\(([^\\)]+?)\\).*","i"),c=l.replace(u,"$1");r[t][1]=parseFloat(c)}}else i[t]&&parseFloat(i[t])&&(r[t][1]=parseFloat(i[t]));o(e,s,""+r[t][1]+a(t))}),r},this.performEnter=function(t,r){var n=(0,m.transformArguments)(e.props.interval,t,r)[0],o=(0,m.transformArguments)(e.props.delay,t,r)[0];e.placeholderTimeoutIds[t]=setTimeout(e.performEnterBegin.bind(e,t,r),n*r+o),e.keysToEnter.indexOf(t)>=0&&e.keysToEnter.splice(e.keysToEnter.indexOf(t),1)},this.performEnterBegin=function(t,r){var n=e.state.childrenShow;n[t]=!0,e.setState({childrenShow:n},e.realPerformEnter.bind(e,t,r))},this.realPerformEnter=function(t,r){var n=(0,y.findDOMNode)(e.refs[t]);if(n){var o=(0,m.transformArguments)(e.props.duration,t,r)[0];b(n,"stop");var a=e.props.enterForcedRePlay?e.getVelocityEnterConfig(t,r):e.getInitAnimType(n,e.getVelocityEnterConfig(t,r));e.props.enterForcedRePlay&&(n.style.visibility="hidden"),b(n,a,{duration:o,easing:e.getVelocityEasing(t,r)[0],visibility:"visible",begin:e.enterBegin.bind(e,t),complete:e.enterComplete.bind(e,t)})}},this.performLeave=function(t,r){clearTimeout(e.placeholderTimeoutIds[t]),delete e.placeholderTimeoutIds[t];var n=(0,y.findDOMNode)(e.refs[t]);if(n){var o=(0,m.transformArguments)(e.props.interval,t,r)[1],a=(0,m.transformArguments)(e.props.delay,t,r)[1],i=(0,m.transformArguments)(e.props.duration,t,r)[1],s=e.props.leaveReverse?e.keysToLeave.length-r-1:r;b(n,"stop"),n.style.visibility="visible";var l=e.getInitAnimType(n,e.getVelocityLeaveConfig(t,r));b(n,l,{delay:o*s+a,duration:i,easing:e.getVelocityEasing(t,r)[1],begin:e.leaveBegin.bind(e,t),complete:e.leaveComplete.bind(e,t)})}},this.enterBegin=function(t,r){r.forEach(function(t){var r=e.props.animatingClassName;t.className=t.className.replace(r[1],""),t.className.indexOf(r[0])===-1&&(t.className+=" "+r[0])})},this.enterComplete=function(t,r){e.keysAnimating.indexOf(t)>=0&&e.keysAnimating.splice(e.keysAnimating.indexOf(t),1),r.forEach(function(t){t.className=t.className.replace(e.props.animatingClassName[0],"").trim()}),e.props.onEnd({key:t,type:"enter"})},this.leaveBegin=function(t,r){r.forEach(function(t){var r=e.props.animatingClassName;t.className=t.className.replace(r[0],""),t.className.indexOf(r[1])===-1&&(t.className+=" "+r[1])})},this.leaveComplete=function(t,r){if(!(e.keysAnimating.indexOf(t)<0)){e.keysAnimating.splice(e.keysAnimating.indexOf(t),1);var n=e.state.childrenShow;n[t]=!1,e.keysToLeave.indexOf(t)>=0&&e.keysToLeave.splice(e.keysToLeave.indexOf(t),1);var o=e.keysToLeave.some(function(e){return n[e]});if(!o){var a=(0,m.toArrayChildren)((0,m.getChildrenFromProps)(e.props));e.setState({children:a,childrenShow:n})}r.forEach(function(t){t.className=t.className.replace(e.props.animatingClassName[1],"").trim()}),e.props.onEnd({key:t,type:"leave"})}}};S.propTypes={component:d.default.PropTypes.any,interval:d.default.PropTypes.any,duration:d.default.PropTypes.any,delay:d.default.PropTypes.any,type:d.default.PropTypes.any,animConfig:d.default.PropTypes.any,ease:d.default.PropTypes.any,leaveReverse:d.default.PropTypes.bool,enterForcedRePlay:d.default.PropTypes.bool,animatingClassName:d.default.PropTypes.array,onEnd:d.default.PropTypes.func,appear:d.default.PropTypes.bool},S.defaultProps={component:"div",interval:100,duration:450,delay:0,type:"right",animConfig:null,ease:"easeOutQuart",leaveReverse:!1,enterForcedRePlay:!1,animatingClassName:["queue-anim-entering","queue-anim-leaving"],onEnd:P,appear:!0},t.default=S,e.exports=t.default}).call(t,r(331).setImmediate)},331:function(e,t,r){"use strict";function n(e,t){this._id=e,this._clearFn=t}var o=Function.prototype.apply;t.setTimeout=function(){return new n(o.call(setTimeout,window,arguments),clearTimeout)},t.setInterval=function(){return new n(o.call(setInterval,window,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},n.prototype.unref=n.prototype.ref=function(){},n.prototype.close=function(){this._clearFn.call(window,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},r(332),t.setImmediate=setImmediate,t.clearImmediate=clearImmediate},332:function(e,t,r){(function(e,t){"use strict";!function(e,r){function n(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r0&&t-1 in e))}if(!e.jQuery){var r=function e(t,r){return new e.fn.init(t,r)};r.isWindow=function(e){return e&&e===e.window},r.type=function(e){return e?"object"===("undefined"==typeof e?"undefined":a(e))||"function"==typeof e?o[s.call(e)]||"object":"undefined"==typeof e?"undefined":a(e):e+""},r.isArray=Array.isArray||function(e){return"array"===r.type(e)},r.isPlainObject=function(e){var t;if(!e||"object"!==r.type(e)||e.nodeType||r.isWindow(e))return!1;try{if(e.constructor&&!i.call(e,"constructor")&&!i.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(e){return!1}for(t in e);return void 0===t||i.call(e,t)},r.each=function(e,r,n){var o,a=0,i=e.length,s=t(e);if(n){if(s)for(;a0?o=i:r=i;while(Math.abs(a)>h&&++s=g?c(t,s):0===l?s:f(t,r,r+w)}function y(){k=!0,e===r&&n===o||p()}var m=4,g=.001,h=1e-7,v=10,b=11,w=1/(b-1),x="Float32Array"in t;if(4!==arguments.length)return!1;for(var P=0;P<4;++P)if("number"!=typeof arguments[P]||isNaN(arguments[P])||!isFinite(arguments[P]))return!1;e=Math.min(e,1),n=Math.min(n,1),e=Math.max(e,0),n=Math.max(n,0);var S=x?new Float32Array(b):new Array(b),k=!1,T=function(t){return k||y(),e===r&&n===o?t:0===t?0:1===t?1:l(d(t),r,o)};T.getControlPoints=function(){return[{x:e,y:r},{x:n,y:o}]};var C="generateBezier("+[e,r,n,o]+")";return T.toString=function(){return C},T}function c(e,t){var r=e;return g.isString(e)?w.Easings[e]||(r=!1):r=g.isArray(e)&&1===e.length?l.apply(null,e):g.isArray(e)&&2===e.length?x.apply(null,e.concat([t])):!(!g.isArray(e)||4!==e.length)&&u.apply(null,e),r===!1&&(r=w.Easings[w.defaults.easing]?w.defaults.easing:b),r}function p(e){if(e){var t=w.timestamp&&e!==!0?e:(new Date).getTime(),r=w.State.calls.length;r>1e4&&(w.State.calls=o(w.State.calls),r=w.State.calls.length);for(var a=0;a4;e--){var t=r.createElement("div");if(t.innerHTML="",t.getElementsByTagName("span").length)return t=null,e}return n}(),m=function(){var e=0;return t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(t){var r,n=(new Date).getTime();return r=Math.max(0,16-(n-e)),e=n+r,setTimeout(function(){t(n+r)},r)}}(),g={isNumber:function(e){return"number"==typeof e},isString:function(e){return"string"==typeof e},isArray:Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNode:function(e){return e&&e.nodeType},isNodeList:function(e){return"object"===("undefined"==typeof e?"undefined":a(e))&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(e))&&e.length!==n&&(0===e.length||"object"===a(e[0])&&e[0].nodeType>0)},isWrapped:function(e){return e&&(g.isArray(e)||g.isNumber(e.length)&&!g.isString(e)&&!g.isFunction(e))},isSVG:function(e){return t.SVGElement&&e instanceof t.SVGElement},isEmptyObject:function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0}},h=!1;if(e.fn&&e.fn.jquery?(d=e,h=!0):d=t.Velocity.Utilities,y<=8&&!h)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(y<=7)return void(jQuery.fn.velocity=jQuery.fn.animate);var v=400,b="swing",w={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:t.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:r.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:d,Redirects:{},Easings:{},Promise:t.Promise,defaults:{queue:"",duration:v,easing:b,begin:n,complete:n,progress:n,display:n,visibility:n,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0,promiseRejectEmpty:!0},init:function(e){d.data(e,"velocity",{isSVG:g.isSVG(e),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:3,patch:2},debug:!1,timestamp:!0};t.pageYOffset!==n?(w.State.scrollAnchor=t,w.State.scrollPropertyLeft="pageXOffset",w.State.scrollPropertyTop="pageYOffset"):(w.State.scrollAnchor=r.documentElement||r.body.parentNode||r.body,w.State.scrollPropertyLeft="scrollLeft",w.State.scrollPropertyTop="scrollTop");var x=function(){function e(e){return-e.tension*e.x-e.friction*e.v}function t(t,r,n){var o={x:t.x+n.dx*r,v:t.v+n.dv*r,tension:t.tension,friction:t.friction};return{dx:o.v,dv:e(o)}}function r(r,n){var o={dx:r.v,dv:e(r)},a=t(r,.5*n,o),i=t(r,.5*n,a),s=t(r,n,i),l=1/6*(o.dx+2*(a.dx+i.dx)+s.dx),u=1/6*(o.dv+2*(a.dv+i.dv)+s.dv);return r.x=r.x+l*n,r.v=r.v+u*n,r}return function e(t,n,o){var a,i,s,l={x:-1,v:0,tension:null,friction:null},u=[0],c=0,p=1e-4,f=.016;for(t=parseFloat(t)||500,n=parseFloat(n)||20,o=o||null,l.tension=t,l.friction=n,a=null!==o,a?(c=e(t,n),i=c/o*f):i=f;;)if(s=r(s||l,i),u.push(1+s.x),c+=16,!(Math.abs(s.x)>p&&Math.abs(s.v)>p))break;return a?function(e){return u[e*(u.length-1)|0]}:c}}();w.Easings={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},spring:function(e){return 1-Math.cos(4.5*e*Math.PI)*Math.exp(6*-e)}},d.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(e,t){w.Easings[t[0]]=u.apply(null,t[1])});var P=w.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var e=0;e=1?"":"alpha(opacity="+parseInt(100*parseFloat(r),10)+")"}else switch(e){case"name":return"opacity";case"extract":return r;case"inject":return r}}},register:function(){function e(e,t,r){var n="border-box"===P.getPropertyValue(t,"boxSizing").toString().toLowerCase();if(n===(r||!1)){var o,a,i=0,s="width"===e?["Left","Right"]:["Top","Bottom"],l=["padding"+s[0],"padding"+s[1],"border"+s[0]+"Width","border"+s[1]+"Width"];for(o=0;o9)||w.State.isGingerbread||(P.Lists.transformsBase=P.Lists.transformsBase.concat(P.Lists.transforms3D));for(var r=0;r8)&&3===a.split(" ").length&&(a+=" 1"),a;case"inject":return/^rgb/.test(o)?o:(y<=8?4===o.split(" ").length&&(o=o.split(/\s+/).slice(0,3).join(" ")):3===o.split(" ").length&&(o+=" 1"),(y<=8?"rgb":"rgba")+"("+o.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")")}}}();P.Normalizations.registered.innerWidth=t("width",!0),P.Normalizations.registered.innerHeight=t("height",!0),P.Normalizations.registered.outerWidth=t("width"),P.Normalizations.registered.outerHeight=t("height")}},Names:{camelCase:function(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})},SVGAttribute:function(e){var t="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(y||w.State.isAndroid&&!w.State.isChrome)&&(t+="|transform"),new RegExp("^("+t+")$","i").test(e)},prefixCheck:function(e){if(w.State.prefixMatches[e])return[w.State.prefixMatches[e],!0];for(var t=["","Webkit","Moz","ms","O"],r=0,n=t.length;r=2&&console.log("Get "+r+": "+l),l},setPropertyValue:function(e,r,n,o,a){var i=r;if("scroll"===r)a.container?a.container["scroll"+a.direction]=n:"Left"===a.direction?t.scrollTo(n,a.alternateValue):t.scrollTo(a.alternateValue,n);else if(P.Normalizations.registered[r]&&"transform"===P.Normalizations.registered[r]("name",e))P.Normalizations.registered[r]("inject",e,n),i="transform",n=s(e).transformCache[r];else{if(P.Hooks.registered[r]){var l=r,u=P.Hooks.getRoot(r);o=o||P.getPropertyValue(e,u),n=P.Hooks.injectValue(l,n,o),r=u}if(P.Normalizations.registered[r]&&(n=P.Normalizations.registered[r]("inject",e,n),r=P.Normalizations.registered[r]("name",e)),i=P.Names.prefixCheck(r)[0],y<=8)try{e.style[i]=n}catch(e){w.debug&&console.log("Browser does not support ["+n+"] for ["+i+"]")}else{var c=s(e);c&&c.isSVG&&P.Names.SVGAttribute(r)?e.setAttribute(r,n):e.style[i]=n}w.debug>=2&&console.log("Set "+r+" ("+i+"): "+n)}return[i,n]},flushTransformCache:function(e){var t="",r=s(e);if((y||w.State.isAndroid&&!w.State.isChrome)&&r&&r.isSVG){var n=function(t){return parseFloat(P.getPropertyValue(e,t))},o={translate:[n("translateX"),n("translateY")],skewX:[n("skewX")],skewY:[n("skewY")],scale:1!==n("scale")?[n("scale"),n("scale")]:[n("scaleX"),n("scaleY")],rotate:[n("rotateZ"),0,0]};d.each(s(e).transformCache,function(e){/^translate/i.test(e)?e="translate":/^scale/i.test(e)?e="scale":/^rotate/i.test(e)&&(e="rotate"),o[e]&&(t+=e+"("+o[e].join(" ")+") ",delete o[e])})}else{var a,i;d.each(s(e).transformCache,function(r){return a=s(e).transformCache[r],"transformPerspective"===r?(i=a,!0):(9===y&&"rotateZ"===r&&(r="rotate"),void(t+=r+a+" "))}),i&&(t="perspective"+i+" "+t)}P.setPropertyValue(e,"transform",t)}};P.Hooks.register(),P.Normalizations.register(),w.hook=function(e,t,r){var o;return e=i(e),d.each(e,function(e,a){if(s(a)===n&&w.init(a),r===n)o===n&&(o=P.getPropertyValue(a,t));else{var i=P.setPropertyValue(a,t,r);"transform"===i[0]&&w.CSS.flushTransformCache(a),o=i}}),o};var S=function e(){function o(){return u?k.promise||null:y}function a(e,o){function a(a){var f,y;if(l.begin&&0===C)try{l.begin.call(h,h)}catch(e){setTimeout(function(){throw e},1)}if("scroll"===O){var m,v,S,V=/^x$/i.test(l.axis)?"Left":"Top",E=parseFloat(l.offset)||0;l.container?g.isWrapped(l.container)||g.isNode(l.container)?(l.container=l.container[0]||l.container,m=l.container["scroll"+V],S=m+d(e).position()[V.toLowerCase()]+E):l.container=null:(m=w.State.scrollAnchor[w.State["scrollProperty"+V]],v=w.State.scrollAnchor[w.State["scrollProperty"+("Left"===V?"Top":"Left")]],S=d(e).offset()[V.toLowerCase()]+E),u={scroll:{rootPropertyValue:!1,startValue:m,currentValue:m,endValue:S,unitType:"",easing:l.easing,scrollData:{container:l.container,direction:V,alternateValue:v}},element:e},w.debug&&console.log("tweensContainer (scroll): ",u.scroll,e)}else if("reverse"===O){if(f=s(e),!f)return;if(!f.tweensContainer)return void d.dequeue(e,l.queue);"none"===f.opts.display&&(f.opts.display="auto"),"hidden"===f.opts.visibility&&(f.opts.visibility="visible"),f.opts.loop=!1,f.opts.begin=null,f.opts.complete=null,x.easing||delete l.easing,x.duration||delete l.duration,l=d.extend({},f.opts,l),y=d.extend(!0,{},f?f.tweensContainer:null);for(var A in y)if(y.hasOwnProperty(A)&&"element"!==A){var N=y[A].startValue;y[A].startValue=y[A].currentValue=y[A].endValue,y[A].endValue=N,g.isEmptyObject(x)||(y[A].easing=l.easing),w.debug&&console.log("reverse tweensContainer ("+A+"): "+JSON.stringify(y[A]),e)}u=y}else if("start"===O){f=s(e),f&&f.tweensContainer&&f.isAnimating===!0&&(y=f.tweensContainer);var F=function(t,r){var n,a,i;return g.isFunction(t)&&(t=t.call(e,o,T)),g.isArray(t)?(n=t[0],!g.isArray(t[1])&&/^[\d-]/.test(t[1])||g.isFunction(t[1])||P.RegEx.isHex.test(t[1])?i=t[1]:g.isString(t[1])&&!P.RegEx.isHex.test(t[1])&&w.Easings[t[1]]||g.isArray(t[1])?(a=r?t[1]:c(t[1],l.duration),i=t[2]):i=t[1]||t[2]):n=t,r||(a=a||l.easing),g.isFunction(n)&&(n=n.call(e,o,T)),g.isFunction(i)&&(i=i.call(e,o,T)),[n||0,a,i]},j=function(o,a){var s,c=P.Hooks.getRoot(o),p=!1,m=a[0],h=a[1],v=a[2];if(!(f&&f.isSVG||"tween"===c||P.Names.prefixCheck(c)[1]!==!1||P.Normalizations.registered[c]!==n))return void(w.debug&&console.log("Skipping ["+c+"] due to a lack of browser support."));(l.display!==n&&null!==l.display&&"none"!==l.display||l.visibility!==n&&"hidden"!==l.visibility)&&/opacity|filter/.test(o)&&!v&&0!==m&&(v=0),l._cacheValues&&y&&y[o]?(v===n&&(v=y[o].endValue+y[o].unitType),p=f.rootPropertyValueCache[c]):P.Hooks.registered[o]?v===n?(p=P.getPropertyValue(e,c),v=P.getPropertyValue(e,o,p)):p=P.Hooks.templates[c][1]:v===n&&(v=P.getPropertyValue(e,o));var b,x,S,k=!1,T=function(e,t){var r,n;return n=(t||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(e){return r=e,""}),r||(r=P.Values.getUnitType(e)),[n,r]};if(g.isString(v)&&g.isString(m)){s="";for(var C=0,V=0,E=[],O=[];C ',E,O,v,m),v=E,m=O,x=S=""):s=n)}s||(b=T(o,v),v=b[0],S=b[1],b=T(o,m),m=b[0].replace(/^([+-\/*])=/,function(e,t){return k=t,""}),x=b[1],v=parseFloat(v)||0,m=parseFloat(m)||0,"%"===x&&(/^(fontSize|lineHeight)$/.test(o)?(m/=100,x="em"):/^scale/.test(o)?(m/=100,x=""):/(Red|Green|Blue)$/i.test(o)&&(m=m/100*255,x="")));var R=function(){var n={myParent:e.parentNode||r.body,position:P.getPropertyValue(e,"position"),fontSize:P.getPropertyValue(e,"fontSize")},o=n.position===L.lastPosition&&n.myParent===L.lastParent,a=n.fontSize===L.lastFontSize;L.lastParent=n.myParent,L.lastPosition=n.position,L.lastFontSize=n.fontSize;var i=100,s={};if(a&&o)s.emToPx=L.lastEmToPx,s.percentToPxWidth=L.lastPercentToPxWidth,s.percentToPxHeight=L.lastPercentToPxHeight;else{var l=f&&f.isSVG?r.createElementNS("http://www.w3.org/2000/svg","rect"):r.createElement("div");w.init(l),n.myParent.appendChild(l),d.each(["overflow","overflowX","overflowY"],function(e,t){w.CSS.setPropertyValue(l,t,"hidden")}),w.CSS.setPropertyValue(l,"position",n.position),w.CSS.setPropertyValue(l,"fontSize",n.fontSize),w.CSS.setPropertyValue(l,"boxSizing","content-box"),d.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(e,t){w.CSS.setPropertyValue(l,t,i+"%")}),w.CSS.setPropertyValue(l,"paddingLeft",i+"em"),s.percentToPxWidth=L.lastPercentToPxWidth=(parseFloat(P.getPropertyValue(l,"width",null,!0))||1)/i,s.percentToPxHeight=L.lastPercentToPxHeight=(parseFloat(P.getPropertyValue(l,"height",null,!0))||1)/i,s.emToPx=L.lastEmToPx=(parseFloat(P.getPropertyValue(l,"paddingLeft"))||1)/i,n.myParent.removeChild(l)}return null===L.remToPx&&(L.remToPx=parseFloat(P.getPropertyValue(r.body,"fontSize"))||16),null===L.vwToPx&&(L.vwToPx=parseFloat(t.innerWidth)/100,L.vhToPx=parseFloat(t.innerHeight)/100),s.remToPx=L.remToPx,s.vwToPx=L.vwToPx,s.vhToPx=L.vhToPx,w.debug>=1&&console.log("Unit ratios: "+JSON.stringify(s),e),s};if(/[\/*]/.test(k))x=S;else if(S!==x&&0!==v)if(0===m)x=S;else{i=i||R();var H=/margin|padding|left|right|width|text|word|letter/i.test(o)||/X$/.test(o)||"x"===o?"x":"y";switch(S){case"%":v*="x"===H?i.percentToPxWidth:i.percentToPxHeight;break;case"px":break;default:v*=i[S+"ToPx"]}switch(x){case"%":v*=1/("x"===H?i.percentToPxWidth:i.percentToPxHeight);break;case"px":break;default:v*=1/i[x+"ToPx"]}}switch(k){case"+":m=v+m;break;case"-":m=v-m;break;case"*":m*=v;break;case"/":m=v/m}u[o]={rootPropertyValue:p,startValue:v,currentValue:v,endValue:m,unitType:x,easing:h},s&&(u[o].pattern=s),w.debug&&console.log("tweensContainer ("+o+"): "+JSON.stringify(u[o]),e)};for(var I in b)if(b.hasOwnProperty(I)){var R=P.Names.camelCase(I),H=F(b[I]);if(P.Lists.colors.indexOf(R)>=0){var z=H[0],q=H[1],B=H[2];if(P.RegEx.isHex.test(z)){for(var _=["Red","Green","Blue"],$=P.Values.hexToRgb(z),W=B?P.Values.hexToRgb(B):n,D=0;D<_.length;D++){var X=[$[D]];q&&X.push(q),W!==n&&X.push(W[D]),j(R+_[D],X)}continue}}j(R,H)}u.element=e}u.element&&(P.Values.addClass(e,"velocity-animating"),M.push(u),f=s(e),f&&(""===l.queue&&(f.tweensContainer=u,f.opts=l),f.isAnimating=!0),C===T-1?(w.State.calls.push([M,h,l,null,k.resolver]),w.State.isTicking===!1&&(w.State.isTicking=!0,p())):C++)}var i,l=d.extend({},w.defaults,x),u={};switch(s(e)===n&&w.init(e),parseFloat(l.delay)&&l.queue!==!1&&d.queue(e,l.queue,function(t){w.velocityQueueEntryFlag=!0,s(e).delayTimer={setTimeout:setTimeout(t,parseFloat(l.delay)),next:t}}),l.duration.toString().toLowerCase()){case"fast":l.duration=200;break;case"normal":l.duration=v;break;case"slow":l.duration=600;break;default:l.duration=parseFloat(l.duration)||1}w.mock!==!1&&(w.mock===!0?l.duration=l.delay=1:(l.duration*=parseFloat(w.mock)||1,l.delay*=parseFloat(w.mock)||1)),l.easing=c(l.easing,l.duration),l.begin&&!g.isFunction(l.begin)&&(l.begin=null),l.progress&&!g.isFunction(l.progress)&&(l.progress=null),l.complete&&!g.isFunction(l.complete)&&(l.complete=null),l.display!==n&&null!==l.display&&(l.display=l.display.toString().toLowerCase(),"auto"===l.display&&(l.display=w.CSS.Values.getDisplayType(e))),l.visibility!==n&&null!==l.visibility&&(l.visibility=l.visibility.toString().toLowerCase()),l.mobileHA=l.mobileHA&&w.State.isMobile&&!w.State.isGingerbread,l.queue===!1?l.delay?setTimeout(a,l.delay):a():d.queue(e,l.queue,function(e,t){return t===!0?(k.promise&&k.resolver(h),!0):(w.velocityQueueEntryFlag=!0,void a(e))}),""!==l.queue&&"fx"!==l.queue||"inprogress"===d.queue(e)[0]||d.dequeue(e)}var l,u,y,m,h,b,x,S=arguments[0]&&(arguments[0].p||d.isPlainObject(arguments[0].properties)&&!arguments[0].properties.names||g.isString(arguments[0].properties));g.isWrapped(this)?(u=!1,m=0,h=this,y=this):(u=!0,m=1,h=S?arguments[0].elements||arguments[0].e:arguments[0]);var k={promise:null,resolver:null,rejecter:null};if(u&&w.Promise&&(k.promise=new w.Promise(function(e,t){k.resolver=e,k.rejecter=t})),S?(b=arguments[0].properties||arguments[0].p,x=arguments[0].options||arguments[0].o):(b=arguments[m],x=arguments[m+1]),h=i(h),!h)return void(k.promise&&(b&&x&&x.promiseRejectEmpty===!1?k.resolver():k.rejecter()));var T=h.length,C=0;if(!/^(stop|finish|finishAll)$/i.test(b)&&!d.isPlainObject(x)){var V=m+1;x={};for(var E=V;E