Repository: cnodejs/nodeclub Branch: master Commit: 91a3286f2c75 Files: 192 Total size: 1.9 MB Directory structure: gitextract_iz_2t4kk/ ├── .gitignore ├── .jshintrc ├── .mention-bot ├── .snyk ├── .travis.yml ├── History.md ├── LICENSE ├── Makefile ├── README.md ├── api/ │ └── v1/ │ ├── message.js │ ├── middleware.js │ ├── reply.js │ ├── tools.js │ ├── topic.js │ ├── topic_collect.js │ └── user.js ├── api_router_v1.js ├── app.js ├── bin/ │ ├── fix_at_problem.js │ ├── fix_topic_collect_count.js │ ├── generate_accesstoken.js │ └── get_user_topics.js ├── common/ │ ├── at.js │ ├── cache.js │ ├── logger.js │ ├── mail.js │ ├── message.js │ ├── redis.js │ ├── render_helper.js │ ├── store.js │ ├── store_local.js │ ├── store_qn.js │ └── tools.js ├── config.default.js ├── controllers/ │ ├── github.js │ ├── message.js │ ├── reply.js │ ├── rss.js │ ├── search.js │ ├── sign.js │ ├── site.js │ ├── static.js │ ├── topic.js │ └── user.js ├── logs/ │ └── .gitkeep ├── middlewares/ │ ├── auth.js │ ├── conf.js │ ├── error_page.js │ ├── github_strategy.js │ ├── limit.js │ ├── mongoose_log.js │ ├── proxy.js │ ├── render.js │ └── request_log.js ├── models/ │ ├── base_model.js │ ├── index.js │ ├── message.js │ ├── reply.js │ ├── topic.js │ ├── topic_collect.js │ └── user.js ├── oneapm.js ├── package.json ├── proxy/ │ ├── index.js │ ├── message.js │ ├── reply.js │ ├── topic.js │ ├── topic_collect.js │ └── user.js ├── public/ │ ├── github-card.html │ ├── javascripts/ │ │ ├── main.js │ │ └── responsive.js │ ├── libs/ │ │ ├── bootstrap/ │ │ │ ├── css/ │ │ │ │ ├── bootstrap-responsive.css │ │ │ │ └── bootstrap.css │ │ │ └── js/ │ │ │ └── bootstrap.js │ │ ├── code-prettify/ │ │ │ ├── lang-apollo.js │ │ │ ├── lang-clj.js │ │ │ ├── lang-css.js │ │ │ ├── lang-go.js │ │ │ ├── lang-hs.js │ │ │ ├── lang-lisp.js │ │ │ ├── lang-lua.js │ │ │ ├── lang-ml.js │ │ │ ├── lang-n.js │ │ │ ├── lang-proto.js │ │ │ ├── lang-scala.js │ │ │ ├── lang-sql.js │ │ │ ├── lang-tex.js │ │ │ ├── lang-vb.js │ │ │ ├── lang-vhdl.js │ │ │ ├── lang-wiki.js │ │ │ ├── lang-xq.js │ │ │ ├── lang-yaml.js │ │ │ ├── prettify.css │ │ │ └── prettify.js │ │ ├── editor/ │ │ │ ├── editor.css │ │ │ ├── editor.js │ │ │ └── ext.js │ │ ├── font-awesome/ │ │ │ ├── css/ │ │ │ │ └── font-awesome.css │ │ │ └── fonts/ │ │ │ └── FontAwesome.otf │ │ ├── jquery-2.1.0.js │ │ ├── jquery-ujs.js │ │ ├── jquery.atwho.js │ │ ├── jquery.caret.js │ │ ├── lodash.compat.js │ │ ├── markdownit.js │ │ ├── qrcode.js │ │ └── webuploader/ │ │ ├── Uploader.swf │ │ ├── webuploader.css │ │ └── webuploader.withoutimage.js │ └── stylesheets/ │ ├── common.css │ ├── jquery.atwho.css │ ├── responsive.css │ └── style.less ├── test/ │ ├── api/ │ │ └── v1/ │ │ ├── message.test.js │ │ ├── reply.test.js │ │ ├── tools.test.js │ │ ├── topic.test.js │ │ ├── topic_collect.test.js │ │ └── user.test.js │ ├── app.test.js │ ├── common/ │ │ ├── at.test.js │ │ ├── cache.test.js │ │ ├── mail.test.js │ │ ├── message.test.js │ │ ├── render_helper.test.js │ │ ├── store_local.test.js │ │ └── tools.test.js │ ├── controllers/ │ │ ├── github.test.js │ │ ├── message.test.js │ │ ├── reply.test.js │ │ ├── rss.test.js │ │ ├── search.test.js │ │ ├── sign.test.js │ │ ├── site.test.js │ │ ├── static.test.js │ │ ├── topic.test.js │ │ └── user.test.js │ ├── env.js │ ├── middlewares/ │ │ ├── conf.test.js │ │ ├── limit.test.js │ │ └── proxy.test.js │ ├── models/ │ │ └── user.test.js │ ├── proxy/ │ │ ├── message.test.js │ │ ├── reply.test.js │ │ ├── topic.test.js │ │ └── user.test.js │ └── support/ │ └── support.js ├── views/ │ ├── _ads.html │ ├── _sponsors.html │ ├── editor_sidebar.html │ ├── includes/ │ │ └── editor.html │ ├── index.html │ ├── layout.html │ ├── message/ │ │ ├── index.html │ │ └── message.html │ ├── notify/ │ │ └── notify.html │ ├── reply/ │ │ ├── edit.html │ │ └── reply.html │ ├── sidebar.html │ ├── sign/ │ │ ├── new_oauth.html │ │ ├── no_github_email.html │ │ ├── reset.html │ │ ├── search_pass.html │ │ ├── sidebar.html │ │ ├── signin.html │ │ └── signup.html │ ├── static/ │ │ ├── about.html │ │ ├── api.html │ │ ├── faq.html │ │ └── getstart.html │ ├── topic/ │ │ ├── _top_good.html │ │ ├── abstract.html │ │ ├── edit.html │ │ ├── index.html │ │ ├── list.html │ │ └── small.html │ └── user/ │ ├── card.html │ ├── collect_topics.html │ ├── followers.html │ ├── followings.html │ ├── index.html │ ├── replies.html │ ├── setting.html │ ├── star.html │ ├── stars.html │ ├── top.html │ ├── top100.html │ ├── top100_user.html │ ├── topics.html │ └── user.html └── web_router.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ config.js .cov coverage node_modules .naeindex coverage.html .monitor *.min.*.js *.min.*.css assets.json # Ignore Mac OS desktop services store .DS_Store # Ignore Windows desktop setting file desktop.ini # Ignore Redis snapshot dump.rdb *.log .idea public/upload/* *.sublime-project *.sublime-workspace *.swp package-lock.json ================================================ FILE: .jshintrc ================================================ { "predef": [ "phantom", "module", "require", "__dirname", "process", "console", "it", "describe", "before", "beforeEach", "after", "afterEach", "ace", "$" ], "browser": true, "node": true, "es5": true, "bitwise": true, "curly": true, "eqeqeq": true, "forin": false, "immed": true, "latedef": true, "newcap": true, "noarg": true, "noempty": true, "nonew": true, "plusplus": false, "undef": true, "strict": false, "trailing": false, "globalstrict": true, "nonstandard": true, "white": true, "indent": 2, "expr": true, "multistr": true, "onevar": false, "unused": "vars" } ================================================ FILE: .mention-bot ================================================ { "userBlacklist": ["huacnlee"] } ================================================ FILE: .snyk ================================================ # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. version: v1.12.0 ignore: {} # patches apply the minimum changes required to fix a vulnerability patch: 'npm:tunnel-agent:20170305': - jpush-sdk > request > tunnel-agent: patched: '2018-07-01T04:07:14.342Z' ================================================ FILE: .travis.yml ================================================ sudo: false language: node_js env: - CXX=g++-4.8 node_js: - stable addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.8 services: - mongodb - redis before_install: - $CXX --version script: make test-cov after_success: npm i codecov && codecov ================================================ FILE: History.md ================================================ 2.1.0 / 2015-09-15 ================== * 使用 oneapm 代替 newrelic 2.0.1 / 2015-08-07 ================== * 去掉【收藏功能】 0.3.6 / 2013-11-22 ================== * fix #237 if topic not exists, do not modified it. * Merge pull request #230 from JacksonTian/fix_null * 修复给空值设置属性的错误 * Merge pull request #224 from JacksonTian/config_ga * 将Google tracker可配置化 * Merge pull request #223 from jiyinyiyong/ga * add Google Analytics * Merge pull request #217 from leizongmin/master * 编辑器进入全屏模式时,调整一下样式 * hotfix sendAtMail * fixed "TypeError: Cannot read property author_id of null" * 使用七牛 gravatar.qiniudn.com 镜像 * Merge pull request #212 from tjwudi/master * Fix 'title' textarea layout problem. * Merge pull request #211 from tjwudi/master * Add OS regular files to .gitignore * Merge pull request #209 from leizongmin/master * 解决打开Topic时自动跳到输入框问题 * Merge pull request #205 from jiyinyiyong/master * Merge pull request #208 from leizongmin/master * 帖子内容页面,增加表格样式 * 发布帖子使用 EpicEditor 编辑器 * xss白名单 增加thead标签 * Merge pull request #204 from JacksonTian/assets_issue * limit the length of message to 20 * 兼容未启用压缩功能的情况 * Merge pull request #193 from dead-horse/fix-block-count * fix block count * Merge pull request #190 from leizongmin/master * 修正用户收藏的话题页面,页码链接不正确问题 * Merge pull request #186 from phoenixlzx/master * Fixed mongoose version * Merge pull request #182 from JacksonTian/assets_mini * 替换debug为mini * Merge pull request #181 from JacksonTian/assets * 用config.debug判断是否线上状态 * Merge pull request #180 from JacksonTian/assets * 升级data2xml * 静态资源重构 * Merge pull request #178 from JacksonTian/typo * Node.JS => Node.js * Merge pull request #177 from JacksonTian/style * 文章页面的样式 * Fix几个样式问题 * fixed #175 stars max size * fixed require package.json nae not support bug * Merge pull request #174 from JacksonTian/style * 去除用标签来制造空格的行为 * 更新样式 * Merge pull request #171 from jiyinyiyong/markdown-p * limit image height * limit pre-wrap inside p * Merge pull request #165 from VitoLau/master * 更新about faq页面结构 * 调整about和faq页面 padding大小 * Merge pull request #164 from jiyinyiyong/master * fix: markdown-text use pre-wrap * format indentations to 2 * limit the with of message links to prevent line breaks * 修复文字过长没有换行的问题 0.3.5 / 2013-05-30 ================== * Update logo based on the new official one (@finian) * UI more flatter; use "white-space:pre" to show spaces (@jiyinyiyong) * fixed #161 xss process after markdown transfer * 修复reply2的逻辑;暂时屏蔽标签功能 * use fixed-width font in reply * a text align and a padding * add padding to the read messages * 点击回复数直接跳到最后一个回复 * 支持html * fixed #154 消息跳转没有直接跳转到回复 * fixed #146 修复tag编辑bug * 增加nae config * 增加邮件提示内容 * read file sync package.json 0.3.4 / 2013-05-27 ================== * user markd instead showdown, use ace (@fengmk2) * 使用加粗的边缘线; 过滤粉红色的边缘线; 加深 panel header 颜色; 去掉 scrollbar 定制 (@jiyinyiyong) * 指定xss模块的配置信息,禁止HTML标签的style和class属性 (@lezongmin) * shanzhai'd Github T3T (@jiyinyiyong) * @jiyinyiyong 修改界面布局 fixed #139 * 添加话题详情主要内容的行高 (@kerngven) * 增加POST提交时间间隔限制 (@leizongmin) * 中英文间用空格 * 发帖页面优化 * 文本框高度不要闪烁 * use bootstrap 2; hide tags * see the demo of new UI * 搜索页面,如果回复时间过长,会产生断行的情况 * unit test cases * #132 Add https:// validate on user.js cnodejs/nodeclub#132 (@meteormatt) * Add 0.10 for travis * fixed #107 update user links 0.3.3 / 2013-03-11 ================== * Merge pull request #126 from cnodejs/updateSignFlow * 修复topic更新bug;修复@某人 bug * reply2也可以定位到 * 修复node 0.6 test cases * 修复删除评论异常 * 修复 exports.updateLastReply 没有callback的bug * 管理员可以帮忙激活账号 * Merge pull request #125 from JacksonTian/refine * Fix http to https * 重构注册和发帖以及发邮件的部分 * Merge pull request #117 from JacksonTian/get_post * 去除掉req.method的判断,分拆方法 * Merge pull request #122 from JacksonTian/proxy * 分离controller和数据操作业务逻辑 * 改完下划线驼峰为小驼峰式风格 * Merge pull request #116 from JacksonTian/codingstyle * Coding style refine. * Merge pull request #113 from JacksonTian/master * 添加依赖服务状态图标 * Merge pull request #112 from JacksonTian/refine * 修正rewire.reset()导致的单元测试异常 * Refine coding style * Merge pull request #111 from JacksonTian/master * Merge pull request #110 from JacksonTian/reset_history * Update Authors * 恢复History.md文件 * Merge pull request #104 from ccding/master * fix issue #27: lower case email address for gravatar * Merge pull request #103 from ccding/master * fix issue #92: email address with gmail label ("+" encode) * fixed topic delete not post method security problem. * empty author * fixed author empty bug * Merge pull request #99 from leizongmin/master * 将Markdown中的H标题解析放到代码块解析后面 * Merge pull request #96 from leizongmin/master * 修正无法正确解析http://127.0.0.1这样的IP地址链接 * fixed font * fixed color style * Merge pull request #87 from jiyinyiyong/rebased * Merge pull request #91 from leizongmin/master * 使用xss模块来过滤主题及回复内容 * update to 0.3.2 * Merge remote branch 'cnode/master' * fix escape * Merge pull request #89 from dead-horse/master * fix test * use in node-validator * Merge remote branch 'cnode/master' * support block code * Merge pull request #88 from dead-horse/master * fix * change @me to markdown * fix @ bug in topic content * Merge pull request #86 from dead-horse/master * not escape html in * add preview * remove tags in topics of home page * some css * 合并通知按钮 * use escape replace of xss() * fixed test cases * Merge pull request #85 from dead-horse/master * 过滤url允许绝对路径 0.3.2 / 2012-03-04 ================== * ensure IncomingForm.UPLOAD_DIR * ensure upload image dir exists * fixed csrf bug in mark message read * remove customHost * add .naeignore files * * merge cnodeclub to nodeclub; * add more settings for custom site; * fixed upload.js not worked bug; * Merge pull request #4 from dead-horse/master * Merge pull request #11 from thebrecht/master * 话题回复数纳入二级回复,样式调整 * 支持table,邮件提醒 * 加入亂數產生新密碼 * fix style * bugs fixed * Merge pull request #3 from LeToNode/master * Merge pull request #6 from ericzhang-cn/master * markdown语法粗体应为两个星号,原描述有误 * Merge pull request #2 from roymax/master * change to async * change to async * Update README.md * 修复`abc+label@gmail.com`格式的注册邮箱不能成功激活的问题 * commit * project init * first commit ================================================ FILE: LICENSE ================================================ (The MIT License) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ TESTS = $(shell find test -type f -name "*.test.js") TEST_TIMEOUT = 10000 MOCHA_REPORTER = spec # NPM_REGISTRY = "--registry=http://registry.npm.taobao.org" NPM_REGISTRY = "" all: test install: @npm install $(NPM_REGISTRY) pretest: @if ! test -f config.js; then \ cp config.default.js config.js; \ fi @if ! test -d public/upload; then \ mkdir public/upload; \ fi test: install pretest @NODE_ENV=test ./node_modules/mocha/bin/mocha \ --reporter $(MOCHA_REPORTER) \ -r should \ -r test/env \ --timeout $(TEST_TIMEOUT) \ $(TESTS) testfile: @NODE_ENV=test ./node_modules/mocha/bin/mocha \ --reporter $(MOCHA_REPORTER) \ -r should \ -r test/env \ --timeout $(TEST_TIMEOUT) \ $(FILE) test-cov cov: install pretest @NODE_ENV=test node \ node_modules/.bin/istanbul cover --preserve-comments \ ./node_modules/.bin/_mocha \ -- \ -r should \ -r test/env \ --reporter $(MOCHA_REPORTER) \ --timeout $(TEST_TIMEOUT) \ $(TESTS) build: @./node_modules/loader-builder/bin/builder views . run: @node app.js start: install build @NODE_ENV=production ./node_modules/.bin/pm2 start app.js -i 0 --name "cnode" --max-memory-restart 400M restart: install build @NODE_ENV=production ./node_modules/.bin/pm2 restart "cnode" .PHONY: install test testfile cov test-cov build run start restart ================================================ FILE: README.md ================================================ Nodeclub = [![build status][travis-image]][travis-url] [![codecov.io][codecov-image]][codecov-url] [![David deps][david-image]][david-url] [![node version][node-image]][node-url] [travis-image]: https://img.shields.io/travis/cnodejs/nodeclub/master.svg?style=flat-square [travis-url]: https://travis-ci.org/cnodejs/nodeclub [codecov-image]: https://img.shields.io/codecov/c/github/cnodejs/nodeclub/master.svg?style=flat-square [codecov-url]: https://codecov.io/github/cnodejs/nodeclub?branch=master [david-image]: https://img.shields.io/david/cnodejs/nodeclub.svg?style=flat-square [david-url]: https://david-dm.org/cnodejs/nodeclub [node-image]: https://img.shields.io/badge/node.js-%3E=_4.2-green.svg?style=flat-square [node-url]: http://nodejs.org/download/ ## 介绍 Nodeclub 是使用 **Node.js** 和 **MongoDB** 开发的社区系统,界面优雅,功能丰富,小巧迅速, 已在Node.js 中文技术社区 [CNode(http://cnodejs.org)](http://cnodejs.org) 得到应用,但你完全可以用它搭建自己的社区。 ## 安装部署 *不保证 Windows 系统的兼容性* 线上跑的是 [Node.js](https://nodejs.org) v8.12.0,[MongoDB](https://www.mongodb.org) 是 v4.0.3,[Redis](http://redis.io) 是 v4.0.9。 ``` 1. 安装 `Node.js[必须]` `MongoDB[必须]` `Redis[必须]` 2. 启动 MongoDB 和 Redis 3. `$ make install` 安装 Nodeclub 的依赖包 4. `cp config.default.js config.js` 请根据需要修改配置文件 5. `$ make test` 确保各项服务都正常 6. `$ node app.js` 7. visit `http://localhost:3000` 8. done! ``` ## 测试 跑测试 ```bash $ make test ``` 跑覆盖率测试 ```bash $ make test-cov ``` ## 贡献 有任何意见或建议都欢迎提 issue,或者直接提给 [@alsotang](https://github.com/alsotang) ## License MIT ================================================ FILE: api/v1/message.js ================================================ var eventproxy = require('eventproxy'); var Message = require('../../proxy').Message; var at = require('../../common/at'); var renderHelper = require('../../common/render_helper'); var _ = require('lodash'); var index = function (req, res, next) { var user_id = req.user._id; var mdrender = req.query.mdrender === 'false' ? false : true; var ep = new eventproxy(); ep.fail(next); ep.all('has_read_messages', 'hasnot_read_messages', function (has_read_messages, hasnot_read_messages) { res.send({ success: true, data: { has_read_messages: has_read_messages, hasnot_read_messages: hasnot_read_messages } }); }); ep.all('has_read', 'unread', function (has_read, unread) { [has_read, unread].forEach(function (msgs, idx) { var epfill = new eventproxy(); epfill.fail(next); epfill.after('message_ready', msgs.length, function (docs) { docs = docs.filter(function (doc) { return !doc.is_invalid; }); docs = docs.map(function (doc) { doc.author = _.pick(doc.author, ['loginname', 'avatar_url']); doc.topic = _.pick(doc.topic, ['id', 'author', 'title', 'last_reply_at']); doc.reply = _.pick(doc.reply, ['id', 'content', 'ups', 'create_at']); if (mdrender) { doc.reply.content = renderHelper.markdown(at.linkUsers(doc.reply.content)); } doc = _.pick(doc, ['id', 'type', 'has_read', 'author', 'topic', 'reply', 'create_at']); return doc; }); ep.emit(idx === 0 ? 'has_read_messages' : 'hasnot_read_messages', docs); }); msgs.forEach(function (doc) { Message.getMessageById(doc._id, epfill.group('message_ready')); }); }); }); Message.getReadMessagesByUserId(user_id, ep.done('has_read')); Message.getUnreadMessageByUserId(user_id, ep.done('unread')); }; exports.index = index; var markAll = function (req, res, next) { var user_id = req.user._id; var ep = new eventproxy(); ep.fail(next); Message.getUnreadMessageByUserId(user_id, ep.done('unread', function (docs) { docs.forEach(function (doc) { doc.has_read = true; doc.save(); }); return docs; })); ep.all('unread', function (unread) { unread = unread.map(function (doc) { doc = _.pick(doc, ['id']); return doc; }); res.send({ success: true, marked_msgs: unread }); }); }; exports.markAll = markAll; var markOne = function (req, res, next) { var msg_id = req.params.msg_id; var ep = new eventproxy(); ep.fail(next); Message.updateOneMessageToRead(msg_id, ep.done('marked_result', function (result) { return result; })); ep.all('marked_result', function (result) { res.send({ success: true, marked_msg_id: msg_id }); }); }; exports.markOne = markOne; var count = function (req, res, next) { var userId = req.user.id; var ep = new eventproxy(); ep.fail(next); Message.getMessagesCount(userId, ep.done(function (count) { res.send({success: true, data: count}); })); }; exports.count = count; ================================================ FILE: api/v1/middleware.js ================================================ var UserModel = require('../../models').User; var eventproxy = require('eventproxy'); var validator = require('validator'); // 非登录用户直接屏蔽 var auth = function (req, res, next) { var ep = new eventproxy(); ep.fail(next); var accessToken = String(req.body.accesstoken || req.query.accesstoken || ''); accessToken = validator.trim(accessToken); UserModel.findOne({accessToken: accessToken}, ep.done(function (user) { if (!user) { res.status(401); return res.send({success: false, error_msg: '错误的accessToken'}); } if (user.is_block) { res.status(403); return res.send({success: false, error_msg: '您的账户被禁用'}); } req.user = user; next(); })); }; exports.auth = auth; // 非登录用户也可通过 var tryAuth = function (req, res, next) { var ep = new eventproxy(); ep.fail(next); var accessToken = String(req.body.accesstoken || req.query.accesstoken || ''); accessToken = validator.trim(accessToken); UserModel.findOne({accessToken: accessToken}, ep.done(function (user) { if (!user) { return next() } if (user.is_block) { res.status(403); return res.send({success: false, error_msg: '您的账户被禁用'}); } req.user = user; next(); })); }; exports.tryAuth = tryAuth; ================================================ FILE: api/v1/reply.js ================================================ var eventproxy = require('eventproxy'); var validator = require('validator'); var Topic = require('../../proxy').Topic; var User = require('../../proxy').User; var Reply = require('../../proxy').Reply; var at = require('../../common/at'); var message = require('../../common/message'); var config = require('../../config'); var create = function (req, res, next) { var topic_id = req.params.topic_id; var content = req.body.content || ''; var reply_id = req.body.reply_id; var ep = new eventproxy(); ep.fail(next); var str = validator.trim(content); if (str === '') { res.status(400); return res.send({success: false, error_msg: '回复内容不能为空'}); } if (!validator.isMongoId(topic_id)) { res.status(400); return res.send({success: false, error_msg: '不是有效的话题id'}); } Topic.getTopic(topic_id, ep.done(function (topic) { if (!topic) { res.status(404); return res.send({success: false, error_msg: '话题不存在'}); } if (topic.lock) { res.status(403); return res.send({success: false, error_msg: '该话题已被锁定'}); } ep.emit('topic', topic); })); ep.all('topic', function (topic) { User.getUserById(topic.author_id, ep.done('topic_author')); }); ep.all('topic', 'topic_author', function (topic, topicAuthor) { Reply.newAndSave(content, topic_id, req.user.id, reply_id, ep.done(function (reply) { Topic.updateLastReply(topic_id, reply._id, ep.done(function () { ep.emit('reply_saved', reply); //发送at消息,并防止重复 at 作者 var newContent = content.replace('@' + topicAuthor.loginname + ' ', ''); at.sendMessageToMentionUsers(newContent, topic_id, req.user.id, reply._id); })); })); User.getUserById(req.user.id, ep.done(function (user) { user.score += 5; user.reply_count += 1; user.save(); ep.emit('score_saved'); })); }); ep.all('reply_saved', 'topic', function (reply, topic) { if (topic.author_id.toString() !== req.user.id.toString()) { message.sendReplyMessage(topic.author_id, req.user.id, topic._id, reply._id); } ep.emit('message_saved'); }); ep.all('reply_saved', 'message_saved', 'score_saved', function (reply) { res.send({ success: true, reply_id: reply._id }); }); }; exports.create = create; var ups = function (req, res, next) { var replyId = req.params.reply_id; var userId = req.user.id; if (!validator.isMongoId(replyId)) { res.status(400); return res.send({success: false, error_msg: '不是有效的评论id'}); } Reply.getReplyById(replyId, function (err, reply) { if (err) { return next(err); } if (!reply) { res.status(404); return res.send({success: false, error_msg: '评论不存在'}); } if (reply.author_id.equals(userId) && !config.debug) { res.status(403); return res.send({success: false, error_msg: '不能帮自己点赞'}); } else { var action; reply.ups = reply.ups || []; var upIndex = reply.ups.indexOf(userId); if (upIndex === -1) { reply.ups.push(userId); action = 'up'; } else { reply.ups.splice(upIndex, 1); action = 'down'; } reply.save(function () { res.send({ success: true, action: action }); }); } }); }; exports.ups = ups; ================================================ FILE: api/v1/tools.js ================================================ var eventproxy = require('eventproxy'); var accesstoken = function (req, res, next) { var ep = new eventproxy(); ep.fail(next); res.send({ success: true, loginname: req.user.loginname, avatar_url: req.user.avatar_url, id: req.user.id }); }; exports.accesstoken = accesstoken; ================================================ FILE: api/v1/topic.js ================================================ var models = require('../../models'); var TopicModel = models.Topic; var TopicProxy = require('../../proxy').Topic; var TopicCollect = require('../../proxy').TopicCollect; var UserProxy = require('../../proxy').User; var UserModel = models.User; var config = require('../../config'); var eventproxy = require('eventproxy'); var _ = require('lodash'); var at = require('../../common/at'); var renderHelper = require('../../common/render_helper'); var validator = require('validator'); var index = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; page = page > 0 ? page : 1; var tab = req.query.tab || 'all'; var limit = Number(req.query.limit) || config.list_topic_count; var mdrender = req.query.mdrender === 'false' ? false : true; var query = {}; if (!tab || tab === 'all') { query.tab = {$nin: ['job', 'dev']} } else { if (tab === 'good') { query.good = true; } else { query.tab = tab; } } query.deleted = false; var options = { skip: (page - 1) * limit, limit: limit, sort: '-top -last_reply_at'}; var ep = new eventproxy(); ep.fail(next); TopicModel.find(query, '', options, ep.done('topics')); ep.all('topics', function (topics) { topics.forEach(function (topic) { UserModel.findById(topic.author_id, ep.done(function (author) { if (mdrender) { topic.content = renderHelper.markdown(at.linkUsers(topic.content)); } topic.author = _.pick(author, ['loginname', 'avatar_url']); ep.emit('author'); })); }); ep.after('author', topics.length, function () { topics = topics.map(function (topic) { return _.pick(topic, ['id', 'author_id', 'tab', 'content', 'title', 'last_reply_at', 'good', 'top', 'reply_count', 'visit_count', 'create_at', 'author']); }); res.send({success: true, data: topics}); }); }); }; exports.index = index; var show = function (req, res, next) { var topicId = String(req.params.id); var mdrender = req.query.mdrender === 'false' ? false : true; var ep = new eventproxy(); if (!validator.isMongoId(topicId)) { res.status(400); return res.send({success: false, error_msg: '不是有效的话题id'}); } ep.fail(next); TopicProxy.getFullTopic(topicId, ep.done(function (msg, topic, author, replies) { if (!topic) { res.status(404); return res.send({success: false, error_msg: '话题不存在'}); } topic.visit_count += 1; topic.save(); topic = _.pick(topic, ['id', 'author_id', 'tab', 'content', 'title', 'last_reply_at', 'good', 'top', 'reply_count', 'visit_count', 'create_at', 'author']); if (mdrender) { topic.content = renderHelper.markdown(at.linkUsers(topic.content)); } topic.author = _.pick(author, ['loginname', 'avatar_url']); topic.replies = replies.map(function (reply) { if (mdrender) { reply.content = renderHelper.markdown(at.linkUsers(reply.content)); } reply.author = _.pick(reply.author, ['loginname', 'avatar_url']); reply = _.pick(reply, ['id', 'author', 'content', 'ups', 'create_at', 'reply_id']); reply.reply_id = reply.reply_id || null; if (reply.ups && req.user && reply.ups.indexOf(req.user._id) != -1) { reply.is_uped = true; } else { reply.is_uped = false; } return reply; }); ep.emit('full_topic', topic) })); if (!req.user) { ep.emitLater('is_collect', null) } else { TopicCollect.getTopicCollect(req.user._id, topicId, ep.done('is_collect')) } ep.all('full_topic', 'is_collect', function (full_topic, is_collect) { full_topic.is_collect = !!is_collect; res.send({success: true, data: full_topic}); }) }; exports.show = show; var create = function (req, res, next) { var title = validator.trim(req.body.title || ''); var tab = validator.trim(req.body.tab || ''); var content = validator.trim(req.body.content || ''); // 得到所有的 tab, e.g. ['ask', 'share', ..] var allTabs = config.tabs.map(function (tPair) { return tPair[0]; }); // 验证 var editError; if (title === '') { editError = '标题不能为空'; } else if (title.length < 5 || title.length > 100) { editError = '标题字数太多或太少'; } else if (!tab || !_.includes(allTabs, tab)) { editError = '必须选择一个版块'; } else if (content === '') { editError = '内容不可为空'; } // END 验证 if (editError) { res.status(400); return res.send({success: false, error_msg: editError}); } TopicProxy.newAndSave(title, content, tab, req.user.id, function (err, topic) { if (err) { return next(err); } var proxy = new eventproxy(); proxy.fail(next); proxy.all('score_saved', function () { res.send({ success: true, topic_id: topic.id }); }); UserProxy.getUserById(req.user.id, proxy.done(function (user) { user.score += 5; user.topic_count += 1; user.save(); req.user = user; proxy.emit('score_saved'); })); //发送at消息 at.sendMessageToMentionUsers(content, topic.id, req.user.id); }); }; exports.create = create; exports.update = function (req, res, next) { var topic_id = _.trim(req.body.topic_id); var title = _.trim(req.body.title); var tab = _.trim(req.body.tab); var content = _.trim(req.body.content); // 得到所有的 tab, e.g. ['ask', 'share', ..] var allTabs = config.tabs.map(function (tPair) { return tPair[0]; }); TopicProxy.getTopicById(topic_id, function (err, topic, tags) { if (!topic) { res.status(400); return res.send({success: false, error_msg: '此话题不存在或已被删除。'}); } if (topic.author_id.equals(req.user._id) || req.user.is_admin) { // 验证 var editError; if (title === '') { editError = '标题不能是空的。'; } else if (title.length < 5 || title.length > 100) { editError = '标题字数太多或太少。'; } else if (!tab || !_.includes(allTabs, tab)) { editError = '必须选择一个版块。'; } // END 验证 if (editError) { return res.send({success: false, error_msg: editError}); } //保存话题 topic.title = title; topic.content = content; topic.tab = tab; topic.update_at = new Date(); topic.save(function (err) { if (err) { return next(err); } //发送at消息 at.sendMessageToMentionUsers(content, topic._id, req.user._id); res.send({ success: true, topic_id: topic.id }); }); } else { res.status(403) return res.send({success: false, error_msg: '对不起,你不能编辑此话题。'}); } }); }; ================================================ FILE: api/v1/topic_collect.js ================================================ var eventproxy = require('eventproxy'); var TopicProxy = require('../../proxy').Topic; var TopicCollectProxy = require('../../proxy').TopicCollect; var UserProxy = require('../../proxy').User; var _ = require('lodash'); var validator = require('validator'); function list(req, res, next) { var loginname = req.params.loginname; var ep = new eventproxy(); ep.fail(next); UserProxy.getUserByLoginName(loginname, ep.done(function (user) { if (!user) { res.status(404); return res.send({success: false, error_msg: '用户不存在'}); } // api 返回 100 条就好了 TopicCollectProxy.getTopicCollectsByUserId(user._id, {limit: 100}, ep.done('collected_topics')); ep.all('collected_topics', function (collected_topics) { var ids = collected_topics.map(function (doc) { return String(doc.topic_id) }); var query = { _id: { '$in': ids } }; TopicProxy.getTopicsByQuery(query, {}, ep.done('topics', function (topics) { topics = _.sortBy(topics, function (topic) { return ids.indexOf(String(topic._id)) }); return topics })); }); ep.all('topics', function (topics) { topics = topics.map(function (topic) { topic.author = _.pick(topic.author, ['loginname', 'avatar_url']); return _.pick(topic, ['id', 'author_id', 'tab', 'content', 'title', 'last_reply_at', 'good', 'top', 'reply_count', 'visit_count', 'create_at', 'author']); }); res.send({success: true, data: topics}) }) })) } exports.list = list; function collect(req, res, next) { var topic_id = req.body.topic_id; if (!validator.isMongoId(topic_id)) { res.status(400); return res.send({success: false, error_msg: '不是有效的话题id'}); } TopicProxy.getTopic(topic_id, function (err, topic) { if (err) { return next(err); } if (!topic) { res.status(404); return res.json({success: false, error_msg: '话题不存在'}); } TopicCollectProxy.getTopicCollect(req.user.id, topic._id, function (err, doc) { if (err) { return next(err); } if (doc) { res.json({success: false}); return; } TopicCollectProxy.newAndSave(req.user.id, topic._id, function (err) { if (err) { return next(err); } res.json({success: true}); }); UserProxy.getUserById(req.user.id, function (err, user) { if (err) { return next(err); } user.collect_topic_count += 1; user.save(); }); topic.collect_count += 1; topic.save(); }); }); } exports.collect = collect; function de_collect(req, res, next) { var topic_id = req.body.topic_id; if (!validator.isMongoId(topic_id)) { res.status(400); return res.send({success: false, error_msg: '不是有效的话题id'}); } TopicProxy.getTopic(topic_id, function (err, topic) { if (err) { return next(err); } if (!topic) { res.status(404); return res.json({success: false, error_msg: '话题不存在'}); } TopicCollectProxy.remove(req.user.id, topic._id, function (err, removeResult) { if (err) { return next(err); } if (removeResult.n == 0) { return res.json({success: false}) } UserProxy.getUserById(req.user.id, function (err, user) { if (err) { return next(err); } user.collect_topic_count -= 1; user.save(); }); topic.collect_count -= 1; topic.save(); res.json({success: true}); }); }); } exports.de_collect = de_collect; ================================================ FILE: api/v1/user.js ================================================ var _ = require('lodash'); var eventproxy = require('eventproxy'); var UserProxy = require('../../proxy').User; var TopicProxy = require('../../proxy').Topic; var ReplyProxy = require('../../proxy').Reply; var TopicCollect = require('../../proxy').TopicCollect; var show = function (req, res, next) { var loginname = req.params.loginname; var ep = new eventproxy(); ep.fail(next); UserProxy.getUserByLoginName(loginname, ep.done(function (user) { if (!user) { res.status(404); return res.send({success: false, error_msg: '用户不存在'}); } var query = {author_id: user._id}; var opt = {limit: 15, sort: '-create_at'}; TopicProxy.getTopicsByQuery(query, opt, ep.done('recent_topics')); ReplyProxy.getRepliesByAuthorId(user._id, {limit: 20, sort: '-create_at'}, ep.done(function (replies) { var topic_ids = replies.map(function (reply) { return reply.topic_id.toString() }); topic_ids = _.uniq(topic_ids).slice(0, 5); // 只显示最近5条 var query = {_id: {'$in': topic_ids}}; var opt = {}; TopicProxy.getTopicsByQuery(query, opt, ep.done('recent_replies', function (recent_replies) { recent_replies = _.sortBy(recent_replies, function (topic) { return topic_ids.indexOf(topic._id.toString()) }); return recent_replies; })); })); ep.all('recent_topics', 'recent_replies', function (recent_topics, recent_replies) { user = _.pick(user, ['loginname', 'avatar_url', 'githubUsername', 'create_at', 'score']); user.recent_topics = recent_topics.map(function (topic) { topic.author = _.pick(topic.author, ['loginname', 'avatar_url']); topic = _.pick(topic, ['id', 'author', 'title', 'last_reply_at']); return topic; }); user.recent_replies = recent_replies.map(function (topic) { topic.author = _.pick(topic.author, ['loginname', 'avatar_url']); topic = _.pick(topic, ['id', 'author', 'title', 'last_reply_at']); return topic; }); res.send({success: true, data: user}); }); })); }; exports.show = show; ================================================ FILE: api_router_v1.js ================================================ var express = require('express'); var topicController = require('./api/v1/topic'); var topicCollectController = require('./api/v1/topic_collect'); var userController = require('./api/v1/user'); var toolsController = require('./api/v1/tools'); var replyController = require('./api/v1/reply'); var messageController = require('./api/v1/message'); var middleware = require('./api/v1/middleware'); var limit = require('./middlewares/limit'); var config = require('./config'); var router = express.Router(); // 主题 router.get('/topics', topicController.index); router.get('/topic/:id', middleware.tryAuth, topicController.show); router.post('/topics', middleware.auth, limit.peruserperday('create_topic', config.create_post_per_day, {showJson: true}), topicController.create); router.post('/topics/update', middleware.auth, topicController.update); // 主题收藏 router.post('/topic_collect/collect', middleware.auth, topicCollectController.collect); // 关注某话题 router.post('/topic_collect/de_collect', middleware.auth, topicCollectController.de_collect); // 取消关注某话题 router.get('/topic_collect/:loginname', topicCollectController.list); // 用户 router.get('/user/:loginname', userController.show); // accessToken 测试 router.post('/accesstoken', middleware.auth, toolsController.accesstoken); // 评论 router.post('/topic/:topic_id/replies', middleware.auth, limit.peruserperday('create_reply', config.create_reply_per_day, {showJson: true}), replyController.create); router.post('/reply/:reply_id/ups', middleware.auth, replyController.ups); // 通知 router.get('/messages', middleware.auth, messageController.index); router.get('/message/count', middleware.auth, messageController.count); router.post('/message/mark_all', middleware.auth, messageController.markAll); router.post('/message/mark_one/:msg_id', middleware.auth, messageController.markOne); module.exports = router; ================================================ FILE: app.js ================================================ /*! * nodeclub - app.js */ /** * Module dependencies. */ var config = require('./config'); if (!config.debug && config.oneapm_key) { require('oneapm'); } require('colors'); var path = require('path'); var Loader = require('loader'); var LoaderConnect = require('loader-connect') var express = require('express'); var session = require('express-session'); var passport = require('passport'); require('./middlewares/mongoose_log'); // 打印 mongodb 查询日志 require('./models'); var GitHubStrategy = require('passport-github').Strategy; var githubStrategyMiddleware = require('./middlewares/github_strategy'); var webRouter = require('./web_router'); var apiRouterV1 = require('./api_router_v1'); var auth = require('./middlewares/auth'); var errorPageMiddleware = require('./middlewares/error_page'); var proxyMiddleware = require('./middlewares/proxy'); var RedisStore = require('connect-redis')(session); var _ = require('lodash'); var csurf = require('csurf'); var compress = require('compression'); var bodyParser = require('body-parser'); var busboy = require('connect-busboy'); var errorhandler = require('errorhandler'); var cors = require('cors'); var requestLog = require('./middlewares/request_log'); var renderMiddleware = require('./middlewares/render'); var logger = require('./common/logger'); var helmet = require('helmet'); var bytes = require('bytes') // 静态文件目录 var staticDir = path.join(__dirname, 'public'); // assets var assets = {}; if (config.mini_assets) { try { assets = require('./assets.json'); } catch (e) { logger.error('You must execute `make build` before start app when mini_assets is true.'); throw e; } } var urlinfo = require('url').parse(config.host); config.hostname = urlinfo.hostname || config.host; var app = express(); // configuration in all env app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'html'); app.engine('html', require('ejs-mate')); app.locals._layoutFile = 'layout.html'; app.enable('trust proxy'); // Request logger。请求时间 app.use(requestLog); if (config.debug) { // 渲染时间 app.use(renderMiddleware.render); } // 静态资源 if (config.debug) { app.use(LoaderConnect.less(__dirname)); // 测试环境用,编译 .less on the fly } app.use('/public', express.static(staticDir)); app.use('/agent', proxyMiddleware.proxy); // 通用的中间件 app.use(require('response-time')()); app.use(helmet.frameguard('sameorigin')); app.use(bodyParser.json({limit: '1mb'})); app.use(bodyParser.urlencoded({ extended: true, limit: '1mb' })); app.use(require('method-override')()); app.use(require('cookie-parser')(config.session_secret)); app.use(compress()); app.use(session({ secret: config.session_secret, store: new RedisStore({ port: config.redis_port, host: config.redis_host, db: config.redis_db, pass: config.redis_password, }), resave: false, saveUninitialized: false, })); // oauth 中间件 app.use(passport.initialize()); // github oauth passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); }); passport.use(new GitHubStrategy(config.GITHUB_OAUTH, githubStrategyMiddleware)); // custom middleware app.use(auth.authUser); app.use(auth.blockUser()); if (!config.debug) { app.use(function (req, res, next) { if (req.path === '/api' || req.path.indexOf('/api') === -1) { csurf()(req, res, next); return; } next(); }); app.set('view cache', true); } // for debug // app.get('/err', function (req, res, next) { // next(new Error('haha')) // }); // set static, dynamic helpers _.extend(app.locals, { config: config, Loader: Loader, assets: assets }); app.use(errorPageMiddleware.errorPage); _.extend(app.locals, require('./common/render_helper')); app.use(function (req, res, next) { res.locals.csrf = req.csrfToken ? req.csrfToken() : ''; next(); }); app.use(busboy({ limits: { fileSize: bytes(config.file_limit) } })); // routes app.use('/api/v1', cors(), apiRouterV1); app.use('/', webRouter); // error handler if (config.debug) { app.use(errorhandler()); } else { app.use(function (err, req, res, next) { logger.error(err); return res.status(500).send('500 status'); }); } if (!module.parent) { app.listen(config.port, function () { logger.info('NodeClub listening on port', config.port); logger.info('God bless love....'); logger.info('You can debug your app with http://' + config.hostname + ':' + config.port); logger.info(''); }); } module.exports = app; ================================================ FILE: bin/fix_at_problem.js ================================================ // 一次性脚本 // 修复之前重复编辑帖子会导致重复 @someone 的渲染问题 var TopicModel = require('../models').Topic; TopicModel.find({content: /\[{2,}@/}).exec(function (err, topics) { topics.forEach(function (topic) { topic.content = fix(topic.content); console.log(topic.id); topic.save(); }); }); function fix(str) { str = str.replace(/\[{1,}(\[@\w+)(\]\(.+?\))\2+/, function (match_text, $1, $2) { return $1 + $2; }); return str; } ================================================ FILE: bin/fix_topic_collect_count.js ================================================ var TopicCollect = require('../models').TopicCollect; var UserModel = require('../models').User; var TopicModel = require('../models').Topic // 修复用户的topic_collect计数 TopicCollect.aggregate( [{ "$group" : { _id : {user_id: "$user_id"}, count : { $sum : 1} } }], function (err, result) { result.forEach(function (row) { var userId = row._id.user_id; var count = row.count; UserModel.findOne({ _id: userId }, function (err, user) { if (!user) { return; } user.collect_topic_count = count; user.save(function () { console.log(user.loginname, count) }); }) }) }) // 修复帖子的topic_collect计数 TopicCollect.aggregate( [{ "$group" : { _id : {topic_id: "$topic_id"}, count : { $sum : 1} } }], function (err, result) { result.forEach(function (row) { var topic_id = row._id.topic_id; var count = row.count; TopicModel.findOne({ _id: topic_id }, function (err, topic) { if (!topic) { return; } topic.collect_topic_count = count; topic.save(function () { console.log(topic.id, count) }); }) }) }) ================================================ FILE: bin/generate_accesstoken.js ================================================ // 一次性脚本 // 为所有老用户生成 accessToken var uuid = require('node-uuid'); var mongoose = require('mongoose'); var config = require('../config'); var async = require('async'); require('../models/user'); mongoose.connect(config.db, function (err) { if (err) { console.error('connect to %s error: ', config.db, err.message); process.exit(1); } }); var UserModel = mongoose.model('User'); var hasRemain = true; async.whilst( function () { return hasRemain; }, function (callback) { UserModel.findOne({accessToken: {$exists: false}}, function (err, user) { if (!user) { hasRemain = false; callback(); return; } user.accessToken = uuid.v4(); user.save(function () { console.log(user.loginname + ' done!'); callback(); }); }); }, function (err) { mongoose.disconnect(); }); ================================================ FILE: bin/get_user_topics.js ================================================ var UserModel = require('../models').User; var TopicModel = require('../models').Topic // usage: // node get_user_topics.js alsotang UserModel.findOne({ loginname: process.argv[2] }, function (err, user) { TopicModel.find({ author_id: user._id }, function (err, topics) { console.log(topics) }) }) ================================================ FILE: common/at.js ================================================ /*! * nodeclub - topic mention user controller. * Copyright(c) 2012 fengmk2 * Copyright(c) 2012 muyuan * MIT Licensed */ /** * Module dependencies. */ var User = require('../proxy').User; var Message = require('./message'); var EventProxy = require('eventproxy'); var _ = require('lodash'); /** * 从文本中提取出@username 标记的用户名数组 * @param {String} text 文本内容 * @return {Array} 用户名数组 */ var fetchUsers = function (text) { if (!text) { return []; } var ignoreRegexs = [ /```.+?```/g, // 去除单行的 ``` /^```[\s\S]+?^```/gm, // ``` 里面的是 pre 标签内容 /`[\s\S]+?`/g, // 同一行中,`some code` 中内容也不该被解析 /^ .*/gm, // 4个空格也是 pre 标签,在这里 . 不会匹配换行 /\b\S*?@[^\s]*?\..+?\b/g, // somebody@gmail.com 会被去除 /\[@.+?\]\(\/.+?\)/g, // 已经被 link 的 username /\/@/g, // 一般是url中path的一部分 ]; ignoreRegexs.forEach(function (ignore_regex) { text = text.replace(ignore_regex, ''); }); var results = text.match(/@[a-z0-9\-_]+\b/igm); var names = []; if (results) { for (var i = 0, l = results.length; i < l; i++) { var s = results[i]; //remove leading char @ s = s.slice(1); names.push(s); } } names = _.uniq(names); return names; }; exports.fetchUsers = fetchUsers; /** * 根据文本内容中读取用户,并发送消息给提到的用户 * Callback: * - err, 数据库异常 * @param {String} text 文本内容 * @param {String} topicId 主题ID * @param {String} authorId 作者ID * @param {String} reply_id 回复ID * @param {Function} callback 回调函数 */ exports.sendMessageToMentionUsers = function (text, topicId, authorId, reply_id, callback) { if (typeof reply_id === 'function') { callback = reply_id; reply_id = null; } callback = callback || _.noop; User.getUsersByNames(fetchUsers(text), function (err, users) { if (err || !users) { return callback(err); } var ep = new EventProxy(); ep.fail(callback); users = users.filter(function (user) { return !user._id.equals(authorId); }); ep.after('sent', users.length, function () { callback(); }); users.forEach(function (user) { Message.sendAtMessage(user._id, authorId, topicId, reply_id, ep.done('sent')); }); }); }; /** * 根据文本内容,替换为数据库中的数据 * Callback: * - err, 数据库异常 * - text, 替换后的文本内容 * @param {String} text 文本内容 * @param {Function} callback 回调函数 */ exports.linkUsers = function (text, callback) { var users = fetchUsers(text); for (var i = 0, l = users.length; i < l; i++) { var name = users[i]; text = text.replace(new RegExp('@' + name + '\\b(?!\\])', 'g'), '[@' + name + '](/user/' + name + ')'); } if (!callback) { return text; } return callback(null, text); }; ================================================ FILE: common/cache.js ================================================ var redis = require('./redis'); var _ = require('lodash'); var logger = require('./logger'); var get = function (key, callback) { var t = new Date(); redis.get(key, function (err, data) { if (err) { return callback(err); } if (!data) { return callback(); } data = JSON.parse(data); var duration = (new Date() - t); logger.debug('Cache', 'get', key, (duration + 'ms').green); callback(null, data); }); }; exports.get = get; // time 参数可选,秒为单位 var set = function (key, value, time, callback) { var t = new Date(); if (typeof time === 'function') { callback = time; time = null; } callback = callback || _.noop; value = JSON.stringify(value); if (!time) { redis.set(key, value, callback); } else { redis.setex(key, time, value, callback); } var duration = (new Date() - t); logger.debug("Cache", "set", key, (duration + 'ms').green); }; exports.set = set; ================================================ FILE: common/logger.js ================================================ var config = require('../config'); var pathLib = require('path') var env = process.env.NODE_ENV || "development" var log4js = require('log4js'); log4js.configure({ appenders: [ { type: 'console' }, { type: 'file', filename: pathLib.join(config.log_dir, 'cheese.log'), category: 'cheese' } ] }); var logger = log4js.getLogger('cheese'); logger.setLevel(config.debug && env !== 'test' ? 'DEBUG' : 'ERROR') module.exports = logger; ================================================ FILE: common/mail.js ================================================ var mailer = require('nodemailer'); var smtpTransport = require('nodemailer-smtp-transport'); var config = require('../config'); var util = require('util'); var logger = require('./logger'); var transporter = mailer.createTransport(smtpTransport(config.mail_opts)); var SITE_ROOT_URL = 'http://' + config.host; var async = require('async') /** * Send an email * @param {Object} data 邮件对象 */ var sendMail = function (data) { if (config.debug) { return; } // 重试5次 async.retry({times: 5}, function (done) { transporter.sendMail(data, function (err) { if (err) { // 写为日志 logger.error('send mail error', err, data); return done(err); } return done() }); }, function (err) { if (err) { return logger.error('send mail finally error', err, data); } logger.info('send mail success', data) }) }; exports.sendMail = sendMail; /** * 发送激活通知邮件 * @param {String} who 接收人的邮件地址 * @param {String} token 重置用的token字符串 * @param {String} name 接收人的用户名 */ exports.sendActiveMail = function (who, token, name) { var from = util.format('%s <%s>', config.name, config.mail_opts.auth.user); var to = who; var subject = config.name + '社区帐号激活'; var html = '

您好:' + name + '

' + '

我们收到您在' + config.name + '社区的注册信息,请点击下面的链接来激活帐户:

' + '激活链接' + '

若您没有在' + config.name + '社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。

' + '

' + config.name + '社区 谨上。

'; exports.sendMail({ from: from, to: to, subject: subject, html: html }); }; /** * 发送密码重置通知邮件 * @param {String} who 接收人的邮件地址 * @param {String} token 重置用的token字符串 * @param {String} name 接收人的用户名 */ exports.sendResetPassMail = function (who, token, name) { var from = util.format('%s <%s>', config.name, config.mail_opts.auth.user); var to = who; var subject = config.name + '社区密码重置'; var html = '

您好:' + name + '

' + '

我们收到您在' + config.name + '社区重置密码的请求,请在24小时内单击下面的链接来重置密码:

' + '重置密码链接' + '

若您没有在' + config.name + '社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。

' + '

' + config.name + '社区 谨上。

'; exports.sendMail({ from: from, to: to, subject: subject, html: html }); }; ================================================ FILE: common/message.js ================================================ var models = require('../models'); var eventproxy = require('eventproxy'); var Message = models.Message; var User = require('../proxy').User; var messageProxy = require('../proxy/message'); var _ = require('lodash'); exports.sendReplyMessage = function (master_id, author_id, topic_id, reply_id, callback) { callback = callback || _.noop; var ep = new eventproxy(); ep.fail(callback); var message = new Message(); message.type = 'reply'; message.master_id = master_id; message.author_id = author_id; message.topic_id = topic_id; message.reply_id = reply_id; message.save(ep.done('message_saved')); ep.all('message_saved', function (msg) { callback(null, msg); }); }; exports.sendAtMessage = function (master_id, author_id, topic_id, reply_id, callback) { callback = callback || _.noop; var ep = new eventproxy(); ep.fail(callback); var message = new Message(); message.type = 'at'; message.master_id = master_id; message.author_id = author_id; message.topic_id = topic_id; message.reply_id = reply_id; message.save(ep.done('message_saved')); ep.all('message_saved', function (msg) { callback(null, msg); }); }; ================================================ FILE: common/redis.js ================================================ var config = require('../config'); var Redis = require('ioredis'); var logger = require('./logger') var client = new Redis({ port: config.redis_port, host: config.redis_host, db: config.redis_db, password: config.redis_password, }); client.on('error', function (err) { if (err) { logger.error('connect to redis error, check your redis config', err); process.exit(1); } }) exports = module.exports = client; ================================================ FILE: common/render_helper.js ================================================ /*! * nodeclub - common/render_helpers.js * Copyright(c) 2013 fengmk2 * MIT Licensed */ "use strict"; /** * Module dependencies. */ var MarkdownIt = require('markdown-it'); var _ = require('lodash'); var config = require('../config'); var validator = require('validator'); var jsxss = require('xss'); var multiline = require('multiline') // Set default options var md = new MarkdownIt(); md.set({ html: false, // Enable HTML tags in source xhtmlOut: false, // Use '/' to close single tags (
) breaks: false, // Convert '\n' in paragraphs into
linkify: true, // Autoconvert URL-like text to links typographer: true, // Enable smartypants and other sweet transforms }); md.renderer.rules.fence = function (tokens, idx) { var token = tokens[idx]; var language = token.info && ('language-' + token.info) || ''; language = validator.escape(language); return '
'
    + '' + validator.escape(token.content) + ''
    + '
'; }; md.renderer.rules.code_block = function (tokens, idx /*, options*/) { var token = tokens[idx]; return '
'
    + '' + validator.escape(token.content) + ''
    + '
'; }; var myxss = new jsxss.FilterXSS({ onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) { // 让 prettyprint 可以工作 if (tag === 'pre' && name === 'class') { return name + '="' + jsxss.escapeAttrValue(value) + '"'; } } }); exports.markdown = function (text) { return '
' + myxss.process(md.render(text || '')) + '
'; }; exports.escapeSignature = function (signature) { return signature.split('\n').map(function (p) { return _.escape(p); }).join('
'); }; exports.staticFile = function (filePath) { if (filePath.indexOf('http') === 0 || filePath.indexOf('//') === 0) { return filePath; } return config.site_static_host + filePath; }; exports.tabName = function (tab) { var pair = _.find(config.tabs, function (pair) { return pair[0] === tab; }); if (pair) { return pair[1]; } }; exports.proxy = function (url) { return url; // 当 google 和 github 封锁严重时,则需要通过服务器代理访问它们的静态资源 // return '/agent?url=' + encodeURIComponent(url); }; // 为了在 view 中使用 exports._ = _; exports.multiline = multiline; ================================================ FILE: common/store.js ================================================ var qn = require('./store_qn'); var local = require('./store_local'); module.exports = qn || local; ================================================ FILE: common/store_local.js ================================================ var config = require('../config'); var utility = require('utility'); var path = require('path'); var fs = require('fs'); exports.upload = function (file, options, callback) { var filename = options.filename; var newFilename = utility.md5(filename + String((new Date()).getTime())) + path.extname(filename); var upload_path = config.upload.path; var base_url = config.upload.url; var filePath = path.join(upload_path, newFilename); var fileUrl = base_url + newFilename; file.on('end', function () { callback(null, { url: fileUrl }); }); file.pipe(fs.createWriteStream(filePath)); }; ================================================ FILE: common/store_qn.js ================================================ var qn = require('qn'); var config = require('../config'); //7牛 client var qnClient = null; if (config.qn_access && config.qn_access.secretKey !== 'your secret key') { qnClient = qn.create(config.qn_access); } module.exports = qnClient; ================================================ FILE: common/tools.js ================================================ var bcrypt = require('bcryptjs'); var moment = require('moment'); moment.locale('zh-cn'); // 使用中文 // 格式化时间 exports.formatDate = function (date, friendly) { date = moment(date); if (friendly) { return date.fromNow(); } else { return date.format('YYYY-MM-DD HH:mm'); } }; exports.validateId = function (str) { return (/^[a-zA-Z0-9\-_]+$/i).test(str); }; exports.bhash = function (str, callback) { bcrypt.hash(str, 10, callback); }; exports.bcompare = function (str, hash, callback) { bcrypt.compare(str, hash, callback); }; ================================================ FILE: config.default.js ================================================ /** * config */ var path = require('path'); var config = { // debug 为 true 时,用于本地调试 debug: true, get mini_assets() { return !this.debug; }, // 是否启用静态文件的合并压缩,详见视图中的Loader name: 'Nodeclub', // 社区名字 description: 'CNode:Node.js专业中文社区', // 社区的描述 keywords: 'nodejs, node, express, connect, socket.io', // 添加到 html head 中的信息 site_headers: [ '' ], site_logo: '/public/images/cnodejs_light.svg', // default is `name` site_icon: '/public/images/cnode_icon_32.png', // 默认没有 favicon, 这里填写网址 // 右上角的导航区 site_navs: [ // 格式 [ path, title, [target=''] ] [ '/about', '关于' ] ], // cdn host,如 http://cnodejs.qiniudn.com site_static_host: '', // 静态文件存储域名 // 社区的域名 host: 'localhost', // 默认的Google tracker ID,自有站点请修改,申请地址:http://www.google.com/analytics/ google_tracker_id: '', // 默认的cnzz tracker ID,自有站点请修改 cnzz_tracker_id: '', // mongodb 配置 db: 'mongodb://127.0.0.1/node_club_dev', // redis 配置,默认是本地 redis_host: '127.0.0.1', redis_port: 6379, redis_db: 0, redis_password: '', session_secret: 'node_club_secret', // 务必修改 auth_cookie_name: 'node_club', // 程序运行的端口 port: 3000, // 话题列表显示的话题数量 list_topic_count: 20, // RSS配置 rss: { title: 'CNode:Node.js专业中文社区', link: 'http://cnodejs.org', language: 'zh-cn', description: 'CNode:Node.js专业中文社区', //最多获取的RSS Item数量 max_rss_items: 50 }, log_dir: path.join(__dirname, 'logs'), // 邮箱配置 mail_opts: { host: 'smtp.126.com', port: 25, auth: { user: 'club@126.com', pass: 'club' }, ignoreTLS: true, }, //weibo app key weibo_key: 10000000, weibo_id: 'your_weibo_id', // admin 可删除话题,编辑标签。把 user_login_name 换成你的登录名 admins: { user_login_name: true }, // github 登陆的配置 GITHUB_OAUTH: { clientID: 'your GITHUB_CLIENT_ID', clientSecret: 'your GITHUB_CLIENT_SECRET', callbackURL: 'http://cnodejs.org/auth/github/callback' }, // 是否允许直接注册(否则只能走 github 的方式) allow_sign_up: true, // oneapm 是个用来监控网站性能的服务 oneapm_key: '', // 下面两个配置都是文件上传的配置 // 7牛的access信息,用于文件上传 qn_access: { accessKey: 'your access key', secretKey: 'your secret key', bucket: 'your bucket name', origin: 'http://your qiniu domain', // 如果vps在国外,请使用 http://up.qiniug.com/ ,这是七牛的国际节点 // 如果在国内,此项请留空 uploadURL: 'http://xxxxxxxx', }, // 文件上传配置 // 注:如果填写 qn_access,则会上传到 7牛,以下配置无效 upload: { path: path.join(__dirname, 'public/upload/'), url: '/public/upload/' }, file_limit: '1MB', // 版块 tabs: [ ['share', '分享'], ['ask', '问答'], ['job', '招聘'], ], // 极光推送 jpush: { appKey: 'YourAccessKeyyyyyyyyyyyy', masterSecret: 'YourSecretKeyyyyyyyyyyyyy', isDebug: false, }, create_post_per_day: 1000, // 每个用户一天可以发的主题数 create_reply_per_day: 1000, // 每个用户一天可以发的评论数 create_user_per_ip: 1000, // 每个 ip 每天可以注册账号的次数 visit_per_day: 1000, // 每个 ip 每天能访问的次数 }; if (process.env.NODE_ENV === 'test') { config.db = 'mongodb://127.0.0.1/node_club_test'; } module.exports = config; ================================================ FILE: controllers/github.js ================================================ var Models = require('../models'); var User = Models.User; var authMiddleWare = require('../middlewares/auth'); var tools = require('../common/tools'); var eventproxy = require('eventproxy'); var uuid = require('node-uuid'); var validator = require('validator'); exports.callback = function (req, res, next) { var profile = req.user; var email = profile.emails && profile.emails[0] && profile.emails[0].value; if (!email) { return res.status(500) .render('sign/no_github_email'); } User.findOne({githubId: profile.id}, function (err, user) { if (err) { return next(err); } // 当用户已经是 cnode 用户时,通过 github 登陆将会更新他的资料 if (user) { user.githubUsername = profile.username; user.githubId = profile.id; user.githubAccessToken = profile.accessToken; // user.loginname = profile.username; user.avatar = profile._json.avatar_url; user.email = email || user.email; user.save(function (err) { if (err) { // 根据 err.err 的错误信息决定如何回应用户,这个地方写得很难看 if (err.message.indexOf('duplicate key error') !== -1) { if (err.message.indexOf('loginname') !== -1) { return res.status(500) .send('您 GitHub 账号的用户名与之前在 CNodejs 注册的用户名重复了'); } } return next(err); } authMiddleWare.gen_session(user, res); return res.redirect('/'); }); } else { // 如果用户还未存在,则建立新用户 req.session.profile = profile; return res.redirect('/auth/github/new'); } }); }; exports.new = function (req, res, next) { res.render('sign/new_oauth', {actionPath: '/auth/github/create'}); }; exports.create = function (req, res, next) { var profile = req.session.profile; var isnew = req.body.isnew; var loginname = validator.trim(req.body.name || '').toLowerCase(); var password = validator.trim(req.body.pass || ''); var ep = new eventproxy(); ep.fail(next); if (!profile) { return res.redirect('/signin'); } delete req.session.profile; var email = profile.emails && profile.emails[0] && profile.emails[0].value; if (!email) { return res.status(500) .render('sign/no_github_email'); } if (isnew) { // 注册新账号 var user = new User({ loginname: profile.username, pass: profile.accessToken, email: email, avatar: profile._json.avatar_url, githubId: profile.id, githubUsername: profile.username, githubAccessToken: profile.accessToken, active: true, accessToken: uuid.v4(), }); user.save(function (err) { if (err) { // 根据 err.err 的错误信息决定如何回应用户,这个地方写得很难看 if (err.message.indexOf('duplicate key error') !== -1) { if (err.message.indexOf('loginname') !== -1) { return res.status(500) .send('您 GitHub 账号的用户名与之前在 CNodejs 注册的用户名重复了'); } } return next(err); // END 根据 err.err 的错误信息决定如何回应用户,这个地方写得很难看 } authMiddleWare.gen_session(user, res); res.redirect('/'); }); } else { // 关联老账号 ep.on('login_error', function (login_error) { res.status(403); res.render('sign/signin', { error: '账号名或密码错误。' }); }); User.findOne({loginname: loginname}, ep.done(function (user) { if (!user) { return ep.emit('login_error'); } tools.bcompare(password, user.pass, ep.done(function (bool) { if (!bool) { return ep.emit('login_error'); } user.githubUsername = profile.username; user.githubId = profile.id; // user.loginname = profile.username; user.avatar = profile._json.avatar_url; user.githubAccessToken = profile.accessToken; user.save(function (err) { if (err) { return next(err); } authMiddleWare.gen_session(user, res); res.redirect('/'); }); })); })); } }; ================================================ FILE: controllers/message.js ================================================ var Message = require('../proxy').Message; var eventproxy = require('eventproxy'); exports.index = function (req, res, next) { var user_id = req.session.user._id; var ep = new eventproxy(); ep.fail(next); ep.all('has_read_messages', 'hasnot_read_messages', function (has_read_messages, hasnot_read_messages) { res.render('message/index', {has_read_messages: has_read_messages, hasnot_read_messages: hasnot_read_messages}); }); ep.all('has_read', 'unread', function (has_read, unread) { [has_read, unread].forEach(function (msgs, idx) { var epfill = new eventproxy(); epfill.fail(next); epfill.after('message_ready', msgs.length, function (docs) { docs = docs.filter(function (doc) { return !doc.is_invalid; }); ep.emit(idx === 0 ? 'has_read_messages' : 'hasnot_read_messages', docs); }); msgs.forEach(function (doc) { Message.getMessageRelations(doc, epfill.group('message_ready')); }); }); Message.updateMessagesToRead(user_id, unread); }); Message.getReadMessagesByUserId(user_id, ep.done('has_read')); Message.getUnreadMessageByUserId(user_id, ep.done('unread')); }; ================================================ FILE: controllers/reply.js ================================================ var validator = require('validator'); var _ = require('lodash'); var at = require('../common/at'); var message = require('../common/message'); var EventProxy = require('eventproxy'); var User = require('../proxy').User; var Topic = require('../proxy').Topic; var Reply = require('../proxy').Reply; var config = require('../config'); /** * 添加回复 */ exports.add = function (req, res, next) { var content = req.body.r_content; var topic_id = req.params.topic_id; var reply_id = req.body.reply_id; var str = validator.trim(String(content)); if (str === '') { return res.renderError('回复内容不能为空!', 422); } var ep = EventProxy.create(); ep.fail(next); Topic.getTopic(topic_id, ep.doneLater(function (topic) { if (!topic) { ep.unbind(); // just 404 page return next(); } if (topic.lock) { return res.status(403).send('此主题已锁定。'); } ep.emit('topic', topic); })); ep.all('topic', function (topic) { User.getUserById(topic.author_id, ep.done('topic_author')); }); ep.all('topic', 'topic_author', function (topic, topicAuthor) { Reply.newAndSave(content, topic_id, req.session.user._id, reply_id, ep.done(function (reply) { Topic.updateLastReply(topic_id, reply._id, ep.done(function () { ep.emit('reply_saved', reply); //发送at消息,并防止重复 at 作者 var newContent = content.replace('@' + topicAuthor.loginname + ' ', ''); at.sendMessageToMentionUsers(newContent, topic_id, req.session.user._id, reply._id); })); })); User.getUserById(req.session.user._id, ep.done(function (user) { user.score += 5; user.reply_count += 1; user.save(); req.session.user = user; ep.emit('score_saved'); })); }); ep.all('reply_saved', 'topic', function (reply, topic) { if (topic.author_id.toString() !== req.session.user._id.toString()) { message.sendReplyMessage(topic.author_id, req.session.user._id, topic._id, reply._id); } ep.emit('message_saved'); }); ep.all('reply_saved', 'message_saved', 'score_saved', function (reply) { res.redirect('/topic/' + topic_id + '#' + reply._id); }); }; /** * 删除回复信息 */ exports.delete = function (req, res, next) { var reply_id = req.body.reply_id; Reply.getReplyById(reply_id, function (err, reply) { if (err) { return next(err); } if (!reply) { res.status(422); res.json({status: 'no reply ' + reply_id + ' exists'}); return; } if (reply.author_id.toString() === req.session.user._id.toString() || req.session.user.is_admin) { reply.deleted = true; reply.save(); res.json({status: 'success'}); reply.author.score -= 5; reply.author.reply_count -= 1; reply.author.save(); } else { res.json({status: 'failed'}); return; } Topic.reduceCount(reply.topic_id, _.noop); }); }; /* 打开回复编辑器 */ exports.showEdit = function (req, res, next) { var reply_id = req.params.reply_id; Reply.getReplyById(reply_id, function (err, reply) { if (!reply) { return res.render404('此回复不存在或已被删除。'); } if (req.session.user._id.equals(reply.author_id) || req.session.user.is_admin) { res.render('reply/edit', { reply_id: reply._id, content: reply.content }); } else { return res.renderError('对不起,你不能编辑此回复。', 403); } }); }; /* 提交编辑回复 */ exports.update = function (req, res, next) { var reply_id = req.params.reply_id; var content = req.body.t_content; Reply.getReplyById(reply_id, function (err, reply) { if (!reply) { return res.render404('此回复不存在或已被删除。'); } if (String(reply.author_id) === req.session.user._id.toString() || req.session.user.is_admin) { if (content.trim().length > 0) { reply.content = content; reply.update_at = new Date(); reply.save(function (err) { if (err) { return next(err); } res.redirect('/topic/' + reply.topic_id + '#' + reply._id); }); } else { return res.renderError('回复的字数太少。', 400); } } else { return res.renderError('对不起,你不能编辑此回复。', 403); } }); }; exports.up = function (req, res, next) { var replyId = req.params.reply_id; var userId = req.session.user._id; Reply.getReplyById(replyId, function (err, reply) { if (err) { return next(err); } if (reply.author_id.equals(userId) && !config.debug) { // 不能帮自己点赞 res.send({ success: false, message: '呵呵,不能帮自己点赞。', }); } else { var action; reply.ups = reply.ups || []; var upIndex = reply.ups.indexOf(userId); if (upIndex === -1) { reply.ups.push(userId); action = 'up'; } else { reply.ups.splice(upIndex, 1); action = 'down'; } reply.save(function () { res.send({ success: true, action: action }); }); } }); }; ================================================ FILE: controllers/rss.js ================================================ var config = require('../config'); var convert = require('data2xml')(); var Topic = require('../proxy').Topic; var cache = require('../common/cache'); var renderHelper = require('../common/render_helper'); var eventproxy = require('eventproxy'); exports.index = function (req, res, next) { if (!config.rss) { res.statusCode = 404; return res.send('Please set `rss` in config.js'); } res.contentType('application/xml'); var ep = new eventproxy(); ep.fail(next); cache.get('rss', ep.done(function (rss) { if (!config.debug && rss) { res.send(rss); } else { var opt = { limit: config.rss.max_rss_items, sort: '-create_at', }; Topic.getTopicsByQuery({tab: {$nin: ['dev']}}, opt, function (err, topics) { if (err) { return next(err); } var rss_obj = { _attr: { version: '2.0' }, channel: { title: config.rss.title, link: config.rss.link, language: config.rss.language, description: config.rss.description, item: [] } }; topics.forEach(function (topic) { rss_obj.channel.item.push({ title: topic.title, link: config.rss.link + '/topic/' + topic._id, guid: config.rss.link + '/topic/' + topic._id, description: renderHelper.markdown(topic.content), author: topic.author.loginname, pubDate: topic.create_at.toUTCString() }); }); var rssContent = convert('rss', rss_obj); rssContent = utf8ForXml(rssContent) cache.set('rss', rssContent, 60 * 5); // 五分钟 res.send(rssContent); }); } })); }; function utf8ForXml(inputStr) { return inputStr.replace(/[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, ''); } ================================================ FILE: controllers/search.js ================================================ exports.index = function (req, res, next) { var q = req.query.q; q = encodeURIComponent(q); res.redirect('https://www.google.com.hk/search?q=site:cnodejs.org+' + q); }; ================================================ FILE: controllers/sign.js ================================================ var validator = require('validator'); var eventproxy = require('eventproxy'); var config = require('../config'); var User = require('../proxy').User; var mail = require('../common/mail'); var tools = require('../common/tools'); var utility = require('utility'); var authMiddleWare = require('../middlewares/auth'); var uuid = require('node-uuid'); //sign up exports.showSignup = function (req, res) { res.render('sign/signup'); }; exports.signup = function (req, res, next) { var loginname = validator.trim(req.body.loginname).toLowerCase(); var email = validator.trim(req.body.email).toLowerCase(); var pass = validator.trim(req.body.pass); var rePass = validator.trim(req.body.re_pass); var ep = new eventproxy(); ep.fail(next); ep.on('prop_err', function (msg) { res.status(422); res.render('sign/signup', {error: msg, loginname: loginname, email: email}); }); // 验证信息的正确性 if ([loginname, pass, rePass, email].some(function (item) { return item === ''; })) { ep.emit('prop_err', '信息不完整。'); return; } if (loginname.length < 5) { ep.emit('prop_err', '用户名至少需要5个字符。'); return; } if (!tools.validateId(loginname)) { return ep.emit('prop_err', '用户名不合法。'); } if (!validator.isEmail(email)) { return ep.emit('prop_err', '邮箱不合法。'); } if (pass !== rePass) { return ep.emit('prop_err', '两次密码输入不一致。'); } // END 验证信息的正确性 User.getUsersByQuery({'$or': [ {'loginname': loginname}, {'email': email} ]}, {}, function (err, users) { if (err) { return next(err); } if (users.length > 0) { ep.emit('prop_err', '用户名或邮箱已被使用。'); return; } tools.bhash(pass, ep.done(function (passhash) { // create gravatar var avatarUrl = User.makeGravatar(email); User.newAndSave(loginname, loginname, passhash, email, avatarUrl, false, function (err) { if (err) { return next(err); } // 发送激活邮件 mail.sendActiveMail(email, utility.md5(email + passhash + config.session_secret), loginname); res.render('sign/signup', { success: '欢迎加入 ' + config.name + '!我们已给您的注册邮箱发送了一封邮件,请点击里面的链接来激活您的帐号。' }); }); })); }); }; /** * Show user login page. * * @param {HttpRequest} req * @param {HttpResponse} res */ exports.showLogin = function (req, res) { req.session._loginReferer = req.headers.referer; res.render('sign/signin'); }; /** * define some page when login just jump to the home page * @type {Array} */ var notJump = [ '/active_account', //active page '/reset_pass', //reset password page, avoid to reset twice '/signup', //regist page '/search_pass' //serch pass page ]; /** * Handle user login. * * @param {HttpRequest} req * @param {HttpResponse} res * @param {Function} next */ exports.login = function (req, res, next) { var loginname = validator.trim(req.body.name).toLowerCase(); var pass = validator.trim(req.body.pass); var ep = new eventproxy(); ep.fail(next); if (!loginname || !pass) { res.status(422); return res.render('sign/signin', { error: '信息不完整。' }); } var getUser; if (loginname.indexOf('@') !== -1) { getUser = User.getUserByMail; } else { getUser = User.getUserByLoginName; } ep.on('login_error', function (login_error) { res.status(403); res.render('sign/signin', { error: '用户名或密码错误' }); }); getUser(loginname, function (err, user) { if (err) { return next(err); } if (!user) { return ep.emit('login_error'); } var passhash = user.pass; tools.bcompare(pass, passhash, ep.done(function (bool) { if (!bool) { return ep.emit('login_error'); } if (!user.active) { // 重新发送激活邮件 mail.sendActiveMail(user.email, utility.md5(user.email + passhash + config.session_secret), user.loginname); res.status(403); return res.render('sign/signin', { error: '此帐号还没有被激活,激活链接已发送到 ' + user.email + ' 邮箱,请查收。' }); } // store session cookie authMiddleWare.gen_session(user, res); //check at some page just jump to home page var refer = req.session._loginReferer || '/'; for (var i = 0, len = notJump.length; i !== len; ++i) { if (refer.indexOf(notJump[i]) >= 0) { refer = '/'; break; } } res.redirect(refer); })); }); }; // sign out exports.signout = function (req, res, next) { req.session.destroy(); res.clearCookie(config.auth_cookie_name, { path: '/' }); res.redirect('/'); }; exports.activeAccount = function (req, res, next) { var key = validator.trim(req.query.key); var name = validator.trim(req.query.name); User.getUserByLoginName(name, function (err, user) { if (err) { return next(err); } if (!user) { return next(new Error('[ACTIVE_ACCOUNT] no such user: ' + name)); } var passhash = user.pass; if (!user || utility.md5(user.email + passhash + config.session_secret) !== key) { return res.render('notify/notify', {error: '信息有误,帐号无法被激活。'}); } if (user.active) { return res.render('notify/notify', {error: '帐号已经是激活状态。'}); } user.active = true; user.save(function (err) { if (err) { return next(err); } res.render('notify/notify', {success: '帐号已被激活,请登录'}); }); }); }; exports.showSearchPass = function (req, res) { res.render('sign/search_pass'); }; exports.updateSearchPass = function (req, res, next) { var email = validator.trim(req.body.email).toLowerCase(); if (!validator.isEmail(email)) { return res.render('sign/search_pass', {error: '邮箱不合法', email: email}); } // 动态生成retrive_key和timestamp到users collection,之后重置密码进行验证 var retrieveKey = uuid.v4(); var retrieveTime = new Date().getTime(); User.getUserByMail(email, function (err, user) { if (!user) { res.render('sign/search_pass', {error: '没有这个电子邮箱。', email: email}); return; } user.retrieve_key = retrieveKey; user.retrieve_time = retrieveTime; user.save(function (err) { if (err) { return next(err); } // 发送重置密码邮件 mail.sendResetPassMail(email, retrieveKey, user.loginname); res.render('notify/notify', {success: '我们已给您填写的电子邮箱发送了一封邮件,请在24小时内点击里面的链接来重置密码。'}); }); }); }; /** * reset password * 'get' to show the page, 'post' to reset password * after reset password, retrieve_key&time will be destroy * @param {http.req} req * @param {http.res} res * @param {Function} next */ exports.resetPass = function (req, res, next) { var key = validator.trim(req.query.key || ''); var name = validator.trim(req.query.name || ''); User.getUserByNameAndKey(name, key, function (err, user) { if (!user) { res.status(403); return res.render('notify/notify', {error: '信息有误,密码无法重置。'}); } var now = new Date().getTime(); var oneDay = 1000 * 60 * 60 * 24; if (!user.retrieve_time || now - user.retrieve_time > oneDay) { res.status(403); return res.render('notify/notify', {error: '该链接已过期,请重新申请。'}); } return res.render('sign/reset', {name: name, key: key}); }); }; exports.updatePass = function (req, res, next) { var psw = validator.trim(req.body.psw) || ''; var repsw = validator.trim(req.body.repsw) || ''; var key = validator.trim(req.body.key) || ''; var name = validator.trim(req.body.name) || ''; var ep = new eventproxy(); ep.fail(next); if (psw !== repsw) { return res.render('sign/reset', {name: name, key: key, error: '两次密码输入不一致。'}); } User.getUserByNameAndKey(name, key, ep.done(function (user) { if (!user) { return res.render('notify/notify', {error: '错误的激活链接'}); } tools.bhash(psw, ep.done(function (passhash) { user.pass = passhash; user.retrieve_key = null; user.retrieve_time = null; user.active = true; // 用户激活 user.save(function (err) { if (err) { return next(err); } return res.render('notify/notify', {success: '你的密码已重置。'}); }); })); })); }; ================================================ FILE: controllers/site.js ================================================ /*! * nodeclub - site index controller. * Copyright(c) 2012 fengmk2 * Copyright(c) 2012 muyuan * MIT Licensed */ /** * Module dependencies. */ var User = require('../proxy').User; var Topic = require('../proxy').Topic; var config = require('../config'); var eventproxy = require('eventproxy'); var cache = require('../common/cache'); var xmlbuilder = require('xmlbuilder'); var renderHelper = require('../common/render_helper'); var _ = require('lodash'); var moment = require('moment'); exports.index = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; page = page > 0 ? page : 1; var tab = req.query.tab || 'all'; var proxy = new eventproxy(); proxy.fail(next); // 取主题 var query = {}; if (!tab || tab === 'all') { query.tab = {$nin: ['job', 'dev']} } else { if (tab === 'good') { query.good = true; } else { query.tab = tab; } } if (!query.good) { query.create_at = {$gte: moment().subtract(1, 'years').toDate()} } var limit = config.list_topic_count; var options = { skip: (page - 1) * limit, limit: limit, sort: '-top -last_reply_at'}; Topic.getTopicsByQuery(query, options, proxy.done('topics', function (topics) { return topics; })); // 取排行榜上的用户 cache.get('tops', proxy.done(function (tops) { if (tops) { proxy.emit('tops', tops); } else { User.getUsersByQuery( {is_block: false}, { limit: 10, sort: '-score'}, proxy.done('tops', function (tops) { cache.set('tops', tops, 60 * 1); return tops; }) ); } })); // END 取排行榜上的用户 // 取0回复的主题 cache.get('no_reply_topics', proxy.done(function (no_reply_topics) { if (no_reply_topics) { proxy.emit('no_reply_topics', no_reply_topics); } else { Topic.getTopicsByQuery( { reply_count: 0, tab: {$nin: ['job', 'dev']}}, { limit: 5, sort: '-create_at'}, proxy.done('no_reply_topics', function (no_reply_topics) { cache.set('no_reply_topics', no_reply_topics, 60 * 1); return no_reply_topics; })); } })); // END 取0回复的主题 // 取分页数据 var pagesCacheKey = JSON.stringify(query) + 'pages'; cache.get(pagesCacheKey, proxy.done(function (pages) { if (pages) { proxy.emit('pages', pages); } else { Topic.getCountByQuery(query, proxy.done(function (all_topics_count) { var pages = Math.ceil(all_topics_count / limit); cache.set(pagesCacheKey, pages, 60 * 1); proxy.emit('pages', pages); })); } })); // END 取分页数据 var tabName = renderHelper.tabName(tab); proxy.all('topics', 'tops', 'no_reply_topics', 'pages', function (topics, tops, no_reply_topics, pages) { res.render('index', { topics: topics, current_page: page, list_topic_count: limit, tops: tops, no_reply_topics: no_reply_topics, pages: pages, tabs: config.tabs, tab: tab, pageTitle: tabName && (tabName + '版块'), }); }); }; exports.sitemap = function (req, res, next) { var urlset = xmlbuilder.create('urlset', {version: '1.0', encoding: 'UTF-8'}); urlset.att('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9'); var ep = new eventproxy(); ep.fail(next); ep.all('sitemap', function (sitemap) { res.type('xml'); res.send(sitemap); }); cache.get('sitemap', ep.done(function (sitemapData) { if (sitemapData) { ep.emit('sitemap', sitemapData); } else { Topic.getLimit5w(function (err, topics) { if (err) { return next(err); } topics.forEach(function (topic) { urlset.ele('url').ele('loc', 'http://cnodejs.org/topic/' + topic._id); }); var sitemapData = urlset.end(); // 缓存一天 cache.set('sitemap', sitemapData, 3600 * 24); ep.emit('sitemap', sitemapData); }); } })); }; exports.appDownload = function (req, res, next) { res.redirect('https://github.com/soliury/noder-react-native/blob/master/README.md') }; ================================================ FILE: controllers/static.js ================================================ var multiline = require('multiline'); // static page // About exports.about = function (req, res, next) { res.render('static/about', { pageTitle: '关于我们' }); }; // FAQ exports.faq = function (req, res, next) { res.render('static/faq'); }; exports.getstart = function (req, res) { res.render('static/getstart', { pageTitle: 'Node.js 新手入门' }); }; exports.robots = function (req, res, next) { res.type('text/plain'); res.send(multiline(function () {; /* # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file # # To ban all spiders from the entire site uncomment the next two lines: # User-Agent: * # Disallow: / */ })); }; exports.api = function (req, res, next) { res.render('static/api'); }; ================================================ FILE: controllers/topic.js ================================================ /*! * nodeclub - controllers/topic.js */ /** * Module dependencies. */ var validator = require('validator'); var at = require('../common/at'); var User = require('../proxy').User; var Topic = require('../proxy').Topic; var TopicCollect = require('../proxy').TopicCollect; var EventProxy = require('eventproxy'); var tools = require('../common/tools'); var store = require('../common/store'); var config = require('../config'); var _ = require('lodash'); var cache = require('../common/cache'); var logger = require('../common/logger') /** * Topic page * * @param {HttpRequest} req * @param {HttpResponse} res * @param {Function} next */ exports.index = function (req, res, next) { function isUped(user, reply) { if (!reply.ups) { return false; } return reply.ups.indexOf(user._id) !== -1; } var topic_id = req.params.tid; var currentUser = req.session.user; if (topic_id.length !== 24) { return res.render404('此话题不存在或已被删除。'); } var events = ['topic', 'other_topics', 'no_reply_topics', 'is_collect']; var ep = EventProxy.create(events, function (topic, other_topics, no_reply_topics, is_collect) { res.render('topic/index', { topic: topic, author_other_topics: other_topics, no_reply_topics: no_reply_topics, is_uped: isUped, is_collect: is_collect, }); }); ep.fail(next); Topic.getFullTopic(topic_id, ep.done(function (message, topic, author, replies) { if (message) { logger.error('getFullTopic error topic_id: ' + topic_id) return res.renderError(message); } topic.visit_count += 1; topic.save(); topic.author = author; topic.replies = replies; // 点赞数排名第三的回答,它的点赞数就是阈值 topic.reply_up_threshold = (function () { var allUpCount = replies.map(function (reply) { return reply.ups && reply.ups.length || 0; }); allUpCount = _.sortBy(allUpCount, Number).reverse(); var threshold = allUpCount[2] || 0; if (threshold < 3) { threshold = 3; } return threshold; })(); ep.emit('topic', topic); // get other_topics var options = { limit: 5, sort: '-last_reply_at'}; var query = { author_id: topic.author_id, _id: { '$nin': [ topic._id ] } }; Topic.getTopicsByQuery(query, options, ep.done('other_topics')); // get no_reply_topics cache.get('no_reply_topics', ep.done(function (no_reply_topics) { if (no_reply_topics) { ep.emit('no_reply_topics', no_reply_topics); } else { Topic.getTopicsByQuery( { reply_count: 0, tab: {$nin: ['job', 'dev']}}, { limit: 5, sort: '-create_at'}, ep.done('no_reply_topics', function (no_reply_topics) { cache.set('no_reply_topics', no_reply_topics, 60 * 1); return no_reply_topics; })); } })); })); if (!currentUser) { ep.emit('is_collect', null); } else { TopicCollect.getTopicCollect(currentUser._id, topic_id, ep.done('is_collect')) } }; exports.create = function (req, res, next) { res.render('topic/edit', { tabs: config.tabs }); }; exports.put = function (req, res, next) { var title = validator.trim(req.body.title); var tab = validator.trim(req.body.tab); var content = validator.trim(req.body.t_content); // 得到所有的 tab, e.g. ['ask', 'share', ..] var allTabs = config.tabs.map(function (tPair) { return tPair[0]; }); // 验证 var editError; if (title === '') { editError = '标题不能是空的。'; } else if (title.length < 5 || title.length > 100) { editError = '标题字数太多或太少。'; } else if (!tab || allTabs.indexOf(tab) === -1) { editError = '必须选择一个版块。'; } else if (content === '') { editError = '内容不可为空'; } // END 验证 if (editError) { res.status(422); return res.render('topic/edit', { edit_error: editError, title: title, content: content, tabs: config.tabs }); } Topic.newAndSave(title, content, tab, req.session.user._id, function (err, topic) { if (err) { return next(err); } var proxy = new EventProxy(); proxy.all('score_saved', function () { res.redirect('/topic/' + topic._id); }); proxy.fail(next); User.getUserById(req.session.user._id, proxy.done(function (user) { user.score += 5; user.topic_count += 1; user.save(); req.session.user = user; proxy.emit('score_saved'); })); //发送at消息 at.sendMessageToMentionUsers(content, topic._id, req.session.user._id); }); }; exports.showEdit = function (req, res, next) { var topic_id = req.params.tid; Topic.getTopicById(topic_id, function (err, topic, tags) { if (!topic) { res.render404('此话题不存在或已被删除。'); return; } if (String(topic.author_id) === String(req.session.user._id) || req.session.user.is_admin) { res.render('topic/edit', { action: 'edit', topic_id: topic._id, title: topic.title, content: topic.content, tab: topic.tab, tabs: config.tabs }); } else { res.renderError('对不起,你不能编辑此话题。', 403); } }); }; exports.update = function (req, res, next) { var topic_id = req.params.tid; var title = req.body.title; var tab = req.body.tab; var content = req.body.t_content; Topic.getTopicById(topic_id, function (err, topic, tags) { if (!topic) { res.render404('此话题不存在或已被删除。'); return; } if (topic.author_id.equals(req.session.user._id) || req.session.user.is_admin) { title = validator.trim(title); tab = validator.trim(tab); content = validator.trim(content); // 验证 var editError; if (title === '') { editError = '标题不能是空的。'; } else if (title.length < 5 || title.length > 100) { editError = '标题字数太多或太少。'; } else if (!tab) { editError = '必须选择一个版块。'; } // END 验证 if (editError) { return res.render('topic/edit', { action: 'edit', edit_error: editError, topic_id: topic._id, content: content, tabs: config.tabs }); } //保存话题 topic.title = title; topic.content = content; topic.tab = tab; topic.update_at = new Date(); topic.save(function (err) { if (err) { return next(err); } //发送at消息 at.sendMessageToMentionUsers(content, topic._id, req.session.user._id); res.redirect('/topic/' + topic._id); }); } else { res.renderError('对不起,你不能编辑此话题。', 403); } }); }; exports.delete = function (req, res, next) { //删除话题, 话题作者topic_count减1 //删除回复,回复作者reply_count减1 //删除topic_collect,用户collect_topic_count减1 var topic_id = req.params.tid; Topic.getFullTopic(topic_id, function (err, err_msg, topic, author, replies) { if (err) { return res.send({ success: false, message: err.message }); } if (!req.session.user.is_admin && !(topic.author_id.equals(req.session.user._id))) { res.status(403); return res.send({success: false, message: '无权限'}); } if (!topic) { res.status(422); return res.send({ success: false, message: '此话题不存在或已被删除。' }); } author.score -= 5; author.topic_count -= 1; author.save(); topic.deleted = true; topic.save(function (err) { if (err) { return res.send({ success: false, message: err.message }); } res.send({ success: true, message: '话题已被删除。' }); }); }); }; // 设为置顶 exports.top = function (req, res, next) { var topic_id = req.params.tid; var referer = req.get('referer'); if (topic_id.length !== 24) { res.render404('此话题不存在或已被删除。'); return; } Topic.getTopic(topic_id, function (err, topic) { if (err) { return next(err); } if (!topic) { res.render404('此话题不存在或已被删除。'); return; } topic.top = !topic.top; topic.save(function (err) { if (err) { return next(err); } var msg = topic.top ? '此话题已置顶。' : '此话题已取消置顶。'; res.render('notify/notify', {success: msg, referer: referer}); }); }); }; // 设为精华 exports.good = function (req, res, next) { var topicId = req.params.tid; var referer = req.get('referer'); Topic.getTopic(topicId, function (err, topic) { if (err) { return next(err); } if (!topic) { res.render404('此话题不存在或已被删除。'); return; } topic.good = !topic.good; topic.save(function (err) { if (err) { return next(err); } var msg = topic.good ? '此话题已加精。' : '此话题已取消加精。'; res.render('notify/notify', {success: msg, referer: referer}); }); }); }; // 锁定主题,不可再回复 exports.lock = function (req, res, next) { var topicId = req.params.tid; var referer = req.get('referer'); Topic.getTopic(topicId, function (err, topic) { if (err) { return next(err); } if (!topic) { res.render404('此话题不存在或已被删除。'); return; } topic.lock = !topic.lock; topic.save(function (err) { if (err) { return next(err); } var msg = topic.lock ? '此话题已锁定。' : '此话题已取消锁定。'; res.render('notify/notify', {success: msg, referer: referer}); }); }); }; // 收藏主题 exports.collect = function (req, res, next) { var topic_id = req.body.topic_id; Topic.getTopic(topic_id, function (err, topic) { if (err) { return next(err); } if (!topic) { res.json({status: 'failed'}); } TopicCollect.getTopicCollect(req.session.user._id, topic._id, function (err, doc) { if (err) { return next(err); } if (doc) { res.json({status: 'failed'}); return; } TopicCollect.newAndSave(req.session.user._id, topic._id, function (err) { if (err) { return next(err); } res.json({status: 'success'}); }); User.getUserById(req.session.user._id, function (err, user) { if (err) { return next(err); } user.collect_topic_count += 1; user.save(); }); req.session.user.collect_topic_count += 1; topic.collect_count += 1; topic.save(); }); }); }; exports.de_collect = function (req, res, next) { var topic_id = req.body.topic_id; Topic.getTopic(topic_id, function (err, topic) { if (err) { return next(err); } if (!topic) { res.json({status: 'failed'}); } TopicCollect.remove(req.session.user._id, topic._id, function (err, removeResult) { if (err) { return next(err); } if (removeResult.n == 0) { return res.json({status: 'failed'}) } User.getUserById(req.session.user._id, function (err, user) { if (err) { return next(err); } user.collect_topic_count -= 1; req.session.user = user; user.save(); }); topic.collect_count -= 1; topic.save(); res.json({status: 'success'}); }); }); }; exports.upload = function (req, res, next) { var isFileLimit = false; req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) { file.on('limit', function () { isFileLimit = true; res.json({ success: false, msg: 'File size too large. Max is ' + config.file_limit }) }); store.upload(file, {filename: filename}, function (err, result) { if (err) { return next(err); } if (isFileLimit) { return; } res.json({ success: true, url: result.url, }); }); }); req.pipe(req.busboy); }; ================================================ FILE: controllers/user.js ================================================ var User = require('../proxy').User; var Topic = require('../proxy').Topic; var Reply = require('../proxy').Reply; var TopicCollect = require('../proxy').TopicCollect; var utility = require('utility'); var util = require('util'); var TopicModel = require('../models').Topic; var ReplyModel = require('../models').Reply; var tools = require('../common/tools'); var config = require('../config'); var EventProxy = require('eventproxy'); var validator = require('validator'); var _ = require('lodash'); var uuid = require('node-uuid') exports.index = function (req, res, next) { var user_name = req.params.name; User.getUserByLoginName(user_name, function (err, user) { if (err) { return next(err); } if (!user) { res.render404('这个用户不存在。'); return; } var render = function (recent_topics, recent_replies) { user.url = (function () { if (user.url && user.url.indexOf('http') !== 0) { return 'http://' + user.url; } return user.url; })(); // 如果用户没有激活,那么管理员可以帮忙激活 var token = ''; if (!user.active && req.session.user && req.session.user.is_admin) { token = utility.md5(user.email + user.pass + config.session_secret); } res.render('user/index', { user: user, recent_topics: recent_topics, recent_replies: recent_replies, token: token, pageTitle: util.format('@%s 的个人主页', user.loginname), }); }; var proxy = new EventProxy(); proxy.assign('recent_topics', 'recent_replies', render); proxy.fail(next); var query = {author_id: user._id}; var opt = {limit: 5, sort: '-create_at'}; Topic.getTopicsByQuery(query, opt, proxy.done('recent_topics')); Reply.getRepliesByAuthorId(user._id, {limit: 20, sort: '-create_at'}, proxy.done(function (replies) { var topic_ids = replies.map(function (reply) { return reply.topic_id.toString() }) topic_ids = _.uniq(topic_ids).slice(0, 5); // 只显示最近5条 var query = {_id: {'$in': topic_ids}}; var opt = {}; Topic.getTopicsByQuery(query, opt, proxy.done('recent_replies', function (recent_replies) { recent_replies = _.sortBy(recent_replies, function (topic) { return topic_ids.indexOf(topic._id.toString()) }) return recent_replies; })); })); }); }; exports.listStars = function (req, res, next) { User.getUsersByQuery({is_star: true}, {}, function (err, stars) { if (err) { return next(err); } res.render('user/stars', {stars: stars}); }); }; exports.showSetting = function (req, res, next) { User.getUserById(req.session.user._id, function (err, user) { if (err) { return next(err); } if (req.query.save === 'success') { user.success = '保存成功。'; } user.error = null; return res.render('user/setting', user); }); }; exports.setting = function (req, res, next) { var ep = new EventProxy(); ep.fail(next); // 显示出错或成功信息 function showMessage(msg, data, isSuccess) { data = data || req.body; var data2 = { loginname: data.loginname, email: data.email, url: data.url, location: data.location, signature: data.signature, weibo: data.weibo, accessToken: data.accessToken, }; if (isSuccess) { data2.success = msg; } else { data2.error = msg; } res.render('user/setting', data2); } // post var action = req.body.action; if (action === 'change_setting') { var url = validator.trim(req.body.url); var location = validator.trim(req.body.location); var weibo = validator.trim(req.body.weibo); var signature = validator.trim(req.body.signature); User.getUserById(req.session.user._id, ep.done(function (user) { user.url = url; user.location = location; user.signature = signature; user.weibo = weibo; user.save(function (err) { if (err) { return next(err); } req.session.user = user.toObject({virtual: true}); return res.redirect('/setting?save=success'); }); })); } if (action === 'change_password') { var old_pass = validator.trim(req.body.old_pass); var new_pass = validator.trim(req.body.new_pass); if (!old_pass || !new_pass) { return res.send('旧密码或新密码不得为空'); } User.getUserById(req.session.user._id, ep.done(function (user) { tools.bcompare(old_pass, user.pass, ep.done(function (bool) { if (!bool) { return showMessage('当前密码不正确。', user); } tools.bhash(new_pass, ep.done(function (passhash) { user.pass = passhash; user.save(function (err) { if (err) { return next(err); } return showMessage('密码已被修改。', user, true); }); })); })); })); } }; exports.toggleStar = function (req, res, next) { var user_id = req.body.user_id; User.getUserById(user_id, function (err, user) { if (err) { return next(err); } if (!user) { return next(new Error('user is not exists')); } user.is_star = !user.is_star; user.save(function (err) { if (err) { return next(err); } res.json({ status: 'success' }); }); }); }; exports.listCollectedTopics = function (req, res, next) { var name = req.params.name; var page = Number(req.query.page) || 1; var limit = config.list_topic_count; User.getUserByLoginName(name, function (err, user) { if (err || !user) { return next(err); } var pages = Math.ceil(user.collect_topic_count/limit); var render = function (topics) { res.render('user/collect_topics', { topics: topics, current_page: page, pages: pages, user: user }); }; var proxy = EventProxy.create('topics', render); proxy.fail(next); var opt = { skip: (page - 1) * limit, limit: limit, }; TopicCollect.getTopicCollectsByUserId(user._id, opt, proxy.done(function (docs) { var ids = docs.map(function (doc) { return String(doc.topic_id) }) var query = { _id: { '$in': ids } }; Topic.getTopicsByQuery(query, {}, proxy.done('topics', function (topics) { topics = _.sortBy(topics, function (topic) { return ids.indexOf(String(topic._id)) }) return topics })); })); }); }; exports.top100 = function (req, res, next) { var opt = {limit: 100, sort: '-score'}; User.getUsersByQuery({is_block: false}, opt, function (err, tops) { if (err) { return next(err); } res.render('user/top100', { users: tops, pageTitle: 'top100', }); }); }; exports.listTopics = function (req, res, next) { var user_name = req.params.name; var page = Number(req.query.page) || 1; var limit = config.list_topic_count; User.getUserByLoginName(user_name, function (err, user) { if (!user) { res.render404('这个用户不存在。'); return; } var render = function (topics, pages) { res.render('user/topics', { user: user, topics: topics, current_page: page, pages: pages }); }; var proxy = new EventProxy(); proxy.assign('topics', 'pages', render); proxy.fail(next); var query = {'author_id': user._id}; var opt = {skip: (page - 1) * limit, limit: limit, sort: '-create_at'}; Topic.getTopicsByQuery(query, opt, proxy.done('topics')); Topic.getCountByQuery(query, proxy.done(function (all_topics_count) { var pages = Math.ceil(all_topics_count / limit); proxy.emit('pages', pages); })); }); }; exports.listReplies = function (req, res, next) { var user_name = req.params.name; var page = Number(req.query.page) || 1; var limit = 50; User.getUserByLoginName(user_name, function (err, user) { if (!user) { res.render404('这个用户不存在。'); return; } var render = function (topics, pages) { res.render('user/replies', { user: user, topics: topics, current_page: page, pages: pages }); }; var proxy = new EventProxy(); proxy.assign('topics', 'pages', render); proxy.fail(next); var opt = {skip: (page - 1) * limit, limit: limit, sort: '-create_at'}; Reply.getRepliesByAuthorId(user._id, opt, proxy.done(function (replies) { // 获取所有有评论的主题 var topic_ids = replies.map(function (reply) { return reply.topic_id.toString(); }); topic_ids = _.uniq(topic_ids); var query = {'_id': {'$in': topic_ids}}; Topic.getTopicsByQuery(query, {}, proxy.done('topics', function (topics) { topics = _.sortBy(topics, function (topic) { return topic_ids.indexOf(topic._id.toString()) }) return topics; })); })); Reply.getCountByAuthorId(user._id, proxy.done('pages', function (count) { var pages = Math.ceil(count / limit); return pages; })); }); }; exports.block = function (req, res, next) { var loginname = req.params.name; var action = req.body.action; var ep = EventProxy.create(); ep.fail(next); User.getUserByLoginName(loginname, ep.done(function (user) { if (!user) { return next(new Error('user is not exists')); } if (action === 'set_block') { ep.all('block_user', function (user) { res.json({status: 'success'}); }); user.is_block = true; user.save(ep.done('block_user')); } else if (action === 'cancel_block') { user.is_block = false; user.save(ep.done(function () { res.json({status: 'success'}); })); } })); }; exports.deleteAll = function (req, res, next) { var loginname = req.params.name; var ep = EventProxy.create(); ep.fail(next); User.getUserByLoginName(loginname, ep.done(function (user) { if (!user) { return next(new Error('user is not exists')); } ep.all('del_topics', 'del_replys', 'del_ups', function () { res.json({status: 'success'}); }); // 删除主题 TopicModel.updateMany({author_id: user._id}, {$set: {deleted: true}}, ep.done('del_topics')); // 删除评论 ReplyModel.updateMany({author_id: user._id}, {$set: {deleted: true}}, ep.done('del_replys')); // 点赞数也全部干掉 ReplyModel.updateMany({}, {$pull: {'ups': user._id}}, ep.done('del_ups')); })); }; exports.refreshToken = function (req, res, next) { var user_id = req.session.user._id; var ep = EventProxy.create(); ep.fail(next); User.getUserById(user_id, ep.done(function (user) { user.accessToken = uuid.v4(); user.save(ep.done(function () { res.json({status: 'success', accessToken: user.accessToken}); })); })); }; ================================================ FILE: logs/.gitkeep ================================================ ================================================ FILE: middlewares/auth.js ================================================ var mongoose = require('mongoose'); var UserModel = mongoose.model('User'); var Message = require('../proxy').Message; var config = require('../config'); var eventproxy = require('eventproxy'); var UserProxy = require('../proxy').User; /** * 需要管理员权限 */ exports.adminRequired = function (req, res, next) { if (!req.session.user) { return res.render('notify/notify', { error: '你还没有登录。' }); } if (!req.session.user.is_admin) { return res.render('notify/notify', { error: '需要管理员权限。' }); } next(); }; /** * 需要登录 */ exports.userRequired = function (req, res, next) { if (!req.session || !req.session.user || !req.session.user._id) { return res.status(403).send('forbidden!'); } next(); }; exports.blockUser = function () { return function (req, res, next) { if (req.path === '/signout') { return next(); } if (req.session.user && req.session.user.is_block && req.method !== 'GET') { return res.status(403).send('您已被管理员屏蔽了。有疑问请联系 @alsotang。'); } next(); }; }; function gen_session(user, res) { var auth_token = user._id + '$$$$'; // 以后可能会存储更多信息,用 $$$$ 来分隔 var opts = { path: '/', maxAge: 1000 * 60 * 60 * 24 * 30, signed: true, httpOnly: true }; res.cookie(config.auth_cookie_name, auth_token, opts); //cookie 有效期30天 } exports.gen_session = gen_session; // 验证用户是否登录 exports.authUser = function (req, res, next) { var ep = new eventproxy(); ep.fail(next); // Ensure current_user always has defined. res.locals.current_user = null; if (config.debug && req.cookies['mock_user']) { var mockUser = JSON.parse(req.cookies['mock_user']); req.session.user = new UserModel(mockUser); if (mockUser.is_admin) { req.session.user.is_admin = true; } return next(); } ep.all('get_user', function (user) { if (!user) { return next(); } user = res.locals.current_user = req.session.user = new UserModel(user); if (config.admins.hasOwnProperty(user.loginname)) { user.is_admin = true; } Message.getMessagesCount(user._id, ep.done(function (count) { user.messages_count = count; next(); })); }); if (req.session.user) { ep.emit('get_user', req.session.user); } else { var auth_token = req.signedCookies[config.auth_cookie_name]; if (!auth_token) { return next(); } var auth = auth_token.split('$$$$'); var user_id = auth[0]; UserProxy.getUserById(user_id, ep.done('get_user')); } }; ================================================ FILE: middlewares/conf.js ================================================ var config = require('../config'); exports.github = function (req, res, next) { if (config.GITHUB_OAUTH.clientID === 'your GITHUB_CLIENT_ID') { return res.send('call the admin to set github oauth.'); } next(); }; ================================================ FILE: middlewares/error_page.js ================================================ // ErrorPage middleware exports.errorPage = function (req, res, next) { res.render404 = function (error) { return res.status(404).render('notify/notify', { error: error }); }; res.renderError = function (error, statusCode) { if (statusCode === undefined) { statusCode = 400; } return res.status(statusCode).render('notify/notify', { error: error }); }; next(); }; ================================================ FILE: middlewares/github_strategy.js ================================================ module.exports = function (accessToken, refreshToken, profile, done) { profile.accessToken = accessToken; done(null, profile); }; ================================================ FILE: middlewares/limit.js ================================================ var config = require('../config'); var cache = require('../common/cache'); var moment = require('moment'); var SEPARATOR = '^_^@T_T'; var makePerDayLimiter = function (identityName, identityFn) { return function (name, limitCount, options) { /* options.showJson = true 表示调用来自API并返回结构化数据;否则表示调用来自前段并渲染错误页面 */ return function (req, res, next) { var identity = identityFn(req); var YYYYMMDD = moment().format('YYYYMMDD'); var key = YYYYMMDD + SEPARATOR + identityName + SEPARATOR + name + SEPARATOR + identity; cache.get(key, function (err, count) { if (err) { return next(err); } count = count || 0; if (count < limitCount) { count += 1; cache.set(key, count, 60 * 60 * 24); res.set('X-RateLimit-Limit', limitCount); res.set('X-RateLimit-Remaining', limitCount - count); next(); } else { res.status(403); if (options.showJson) { res.send({success: false, error_msg: '频率限制:当前操作每天可以进行 ' + limitCount + ' 次'}); } else { res.render('notify/notify', { error: '频率限制:当前操作每天可以进行 ' + limitCount + ' 次'}); } } }); }; }; }; exports.peruserperday = makePerDayLimiter('peruserperday', function (req) { return (req.user || req.session.user).loginname; }); exports.peripperday = makePerDayLimiter('peripperday', function (req) { var realIP = req.get('x-real-ip'); if (!realIP && !config.debug) { throw new Error('should provide `x-real-ip` header') } return realIP; }); ================================================ FILE: middlewares/mongoose_log.js ================================================ var mongoose = require('mongoose'); var logger = require('../common/logger'); var config = require('../config'); if (config.debug) { var traceMQuery = function (method, info, query) { return function (err, result, millis) { if (err) { logger.error('traceMQuery error:', err) } var infos = []; infos.push(query._collection.collection.name + "." + method.blue); infos.push(JSON.stringify(info)); infos.push((millis + 'ms').green); logger.debug("MONGO".magenta, infos.join(' ')); }; }; mongoose.Mongoose.prototype.mquery.setGlobalTraceFunction(traceMQuery); } ================================================ FILE: middlewares/proxy.js ================================================ var urllib = require('url'); var request = require('request'); var logger = require('../common/logger') var _ = require('lodash') var ALLOW_HOSTNAME = [ 'avatars.githubusercontent.com', 'www.gravatar.com', 'gravatar.com', 'www.google-analytics.com', ]; exports.proxy = function (req, res, next) { var url = decodeURIComponent(req.query.url); var hostname = urllib.parse(url).hostname; if (ALLOW_HOSTNAME.indexOf(hostname) === -1) { return res.send(hostname + ' is not allowed'); } request.get({ url: url, headers: _.omit(req.headers, ['cookie', 'refer']), }) .on('response', function (response) { res.set(response.headers); }) .on('error', function (err) { logger.error(err); }) .pipe(res); }; ================================================ FILE: middlewares/render.js ================================================ var logger = require('../common/logger'); // Patch res.render method to output logger exports.render = function (req, res, next) { res._render = res.render; res.render = function (view, options, fn) { var t = new Date(); res._render(view, options, fn); var duration = (new Date() - t); logger.info("Render view", view, ("(" + duration + "ms)").green); }; next(); }; ================================================ FILE: middlewares/request_log.js ================================================ var logger = require('../common/logger'); var ignore = /^\/(public|agent)/; exports = module.exports = function (req, res, next) { // Assets do not out log. if (ignore.test(req.url)) { next(); return; } var t = new Date(); logger.info('\n\nStarted', t.toISOString(), req.method, req.url, req.ip); res.on('finish', function () { var duration = ((new Date()) - t); logger.info('Completed', res.statusCode, ('(' + duration + 'ms)').green); }); next(); }; ================================================ FILE: models/base_model.js ================================================ /** * 给所有的 Model 扩展功能 * http://mongoosejs.com/docs/plugins.html */ var tools = require('../common/tools'); module.exports = function (schema) { schema.methods.create_at_ago = function () { return tools.formatDate(this.create_at, true); }; schema.methods.update_at_ago = function () { return tools.formatDate(this.update_at, true); }; }; ================================================ FILE: models/index.js ================================================ var mongoose = require('mongoose'); var config = require('../config'); var logger = require('../common/logger') mongoose.connect(config.db, { poolSize: 20, useCreateIndex: true, useNewUrlParser: true }, function (err) { if (err) { logger.error('connect to %s error: ', config.db, err.message); process.exit(1); } }); // models require('./user'); require('./topic'); require('./reply'); require('./topic_collect'); require('./message'); exports.User = mongoose.model('User'); exports.Topic = mongoose.model('Topic'); exports.Reply = mongoose.model('Reply'); exports.TopicCollect = mongoose.model('TopicCollect'); exports.Message = mongoose.model('Message'); ================================================ FILE: models/message.js ================================================ var mongoose = require('mongoose'); var BaseModel = require("./base_model"); var Schema = mongoose.Schema; var ObjectId = Schema.ObjectId; /* * type: * reply: xx 回复了你的话题 * reply2: xx 在话题中回复了你 * follow: xx 关注了你 * at: xx @了你 */ var MessageSchema = new Schema({ type: { type: String }, master_id: { type: ObjectId}, author_id: { type: ObjectId }, topic_id: { type: ObjectId }, reply_id: { type: ObjectId }, has_read: { type: Boolean, default: false }, create_at: { type: Date, default: Date.now } }); MessageSchema.plugin(BaseModel); MessageSchema.index({master_id: 1, has_read: -1, create_at: -1}); mongoose.model('Message', MessageSchema); ================================================ FILE: models/reply.js ================================================ var mongoose = require('mongoose'); var BaseModel = require("./base_model"); var Schema = mongoose.Schema; var ObjectId = Schema.ObjectId; var ReplySchema = new Schema({ content: { type: String }, topic_id: { type: ObjectId}, author_id: { type: ObjectId }, reply_id: { type: ObjectId }, create_at: { type: Date, default: Date.now }, update_at: { type: Date, default: Date.now }, content_is_html: { type: Boolean }, ups: [Schema.Types.ObjectId], deleted: {type: Boolean, default: false}, }); ReplySchema.plugin(BaseModel); ReplySchema.index({topic_id: 1}); ReplySchema.index({author_id: 1, create_at: -1}); mongoose.model('Reply', ReplySchema); ================================================ FILE: models/topic.js ================================================ var mongoose = require('mongoose'); var BaseModel = require("./base_model"); var Schema = mongoose.Schema; var ObjectId = Schema.ObjectId; var config = require('../config'); var _ = require('lodash'); var TopicSchema = new Schema({ title: { type: String }, content: { type: String }, author_id: { type: ObjectId }, top: { type: Boolean, default: false }, // 置顶帖 good: {type: Boolean, default: false}, // 精华帖 lock: {type: Boolean, default: false}, // 被锁定主题 reply_count: { type: Number, default: 0 }, visit_count: { type: Number, default: 0 }, collect_count: { type: Number, default: 0 }, create_at: { type: Date, default: Date.now }, update_at: { type: Date, default: Date.now }, last_reply: { type: ObjectId }, last_reply_at: { type: Date, default: Date.now }, content_is_html: { type: Boolean }, tab: {type: String}, deleted: {type: Boolean, default: false}, }); TopicSchema.plugin(BaseModel); TopicSchema.index({create_at: -1}); TopicSchema.index({top: -1, last_reply_at: -1}); TopicSchema.index({author_id: 1, create_at: -1}); TopicSchema.virtual('tabName').get(function () { var tab = this.tab; var pair = _.find(config.tabs, function (_pair) { return _pair[0] === tab; }); if (pair) { return pair[1]; } else { return ''; } }); mongoose.model('Topic', TopicSchema); ================================================ FILE: models/topic_collect.js ================================================ var mongoose = require('mongoose'); var BaseModel = require("./base_model"); var Schema = mongoose.Schema; var ObjectId = Schema.ObjectId; var TopicCollectSchema = new Schema({ user_id: { type: ObjectId }, topic_id: { type: ObjectId }, create_at: { type: Date, default: Date.now } }); TopicCollectSchema.plugin(BaseModel); TopicCollectSchema.index({user_id: 1, topic_id: 1}, {unique: true}); mongoose.model('TopicCollect', TopicCollectSchema); ================================================ FILE: models/user.js ================================================ var mongoose = require('mongoose'); var BaseModel = require("./base_model"); var renderHelper = require('../common/render_helper'); var Schema = mongoose.Schema; var utility = require('utility'); var _ = require('lodash'); var UserSchema = new Schema({ name: { type: String}, loginname: { type: String}, pass: { type: String }, email: { type: String}, url: { type: String }, profile_image_url: {type: String}, location: { type: String }, signature: { type: String }, profile: { type: String }, weibo: { type: String }, avatar: { type: String }, githubId: { type: String}, githubUsername: {type: String}, githubAccessToken: {type: String}, is_block: {type: Boolean, default: false}, score: { type: Number, default: 0 }, topic_count: { type: Number, default: 0 }, reply_count: { type: Number, default: 0 }, follower_count: { type: Number, default: 0 }, following_count: { type: Number, default: 0 }, collect_tag_count: { type: Number, default: 0 }, collect_topic_count: { type: Number, default: 0 }, create_at: { type: Date, default: Date.now }, update_at: { type: Date, default: Date.now }, is_star: { type: Boolean }, level: { type: String }, active: { type: Boolean, default: false }, receive_reply_mail: {type: Boolean, default: false }, receive_at_mail: { type: Boolean, default: false }, from_wp: { type: Boolean }, retrieve_time: {type: Number}, retrieve_key: {type: String}, accessToken: {type: String}, }); UserSchema.plugin(BaseModel); UserSchema.virtual('avatar_url').get(function () { var url = this.avatar || ('https://gravatar.com/avatar/' + utility.md5(this.email.toLowerCase()) + '?size=48'); // www.gravatar.com 被墙 url = url.replace('www.gravatar.com', 'gravatar.com'); // 让协议自适应 protocol,使用 `//` 开头 if (url.indexOf('http:') === 0) { url = url.slice(5); } // 如果是 github 的头像,则限制大小 if (url.indexOf('githubusercontent') !== -1) { url += '&s=120'; } return url; }); UserSchema.virtual('isAdvanced').get(function () { // 积分高于 700 则认为是高级用户 return this.score > 700 || this.is_star; }); UserSchema.index({loginname: 1}, {unique: true}); UserSchema.index({email: 1}, {unique: true}); UserSchema.index({score: -1}); UserSchema.index({githubId: 1}); UserSchema.index({accessToken: 1}); UserSchema.pre('save', function(next){ var now = new Date(); this.update_at = now; next(); }); mongoose.model('User', UserSchema); ================================================ FILE: oneapm.js ================================================ /** * OneAPM agent configuration. * * See lib/config.defaults.js in the agent distribution for a more complete * description of configuration variables and their potential values. */ var config = require('./config'); exports.config = { /** * Array of application names. */ app_name : [config.name], /** * Your OneAPM license key. */ license_key : config.oneapm_key, logging : { /** * Level at which to log. 'trace' is most useful to OneAPM when diagnosing * issues with the agent, 'info' and higher will impose the least overhead on * production applications. */ level : 'info' }, transaction_events: { enabled: true } }; ================================================ FILE: package.json ================================================ { "name": "nodeclub", "version": "2.1.1", "private": true, "main": "app.js", "description": "A Node.js bbs using MongoDB", "repository": "https://github.com/cnodejs/nodeclub", "dependencies": { "async": "1.5.2", "bcryptjs": "2.3.0", "body-parser": "1.17.1", "bytes": "^2.2.0", "colors": "1.1.2", "compression": "1.7.0", "connect-busboy": "0.0.2", "connect-redis": "3.0.2", "cookie-parser": "1.4.1", "cors": "2.7.1", "csurf": "1.8.3", "data2xml": "1.2.4", "ejs-mate": "2.3.0", "eventproxy": "1.0.0", "express": "4.16.0", "express-session": "1.12.1", "helmet": "1.3.0", "ioredis": "2.0.0", "jpush-sdk": "3.3.2", "loader-builder": "2.4.1", "loader": "2.1.1", "lodash": "4.17.21", "log4js": "^0.6.29", "markdown-it": "6.0.0", "memory-cache": "0.1.4", "method-override": "2.3.5", "moment": "2.15.2", "mongoose": "5.3.9", "multiline": "1.0.2", "node-uuid": "1.4.7", "nodemailer": "2.3.0", "nodemailer-smtp-transport": "2.4.0", "oneapm": "1.2.20", "passport": "0.3.2", "passport-github": "1.1.0", "pm2": "*", "qn": "1.3.0", "ready": "0.1.1", "request": "2.81.0", "response-time": "2.3.1", "superagent": "2.0.0", "utility": "1.6.0", "validator": "5.1.0", "xmlbuilder": "7.0.0", "xss": "0.2.10", "snyk": "^1.88.0" }, "devDependencies": { "errorhandler": "1.4.3", "istanbul": "0.4.2", "loader-connect": "1.0.1", "mm": "1.3.5", "mocha": "2.4.5", "nock": "7.5.0", "pedding": "1.0.0", "should": "8.3.0", "supertest": "1.2.0" }, "scripts": { "test": "make test", "snyk-protect": "snyk protect", "prepare": "npm run snyk-protect" }, "snyk": true } ================================================ FILE: proxy/index.js ================================================ exports.User = require('./user'); exports.Message = require('./message'); exports.Topic = require('./topic'); exports.Reply = require('./reply'); exports.TopicCollect = require('./topic_collect'); ================================================ FILE: proxy/message.js ================================================ var EventProxy = require('eventproxy'); var _ = require('lodash'); var Message = require('../models').Message; var User = require('./user'); var Topic = require('./topic'); var Reply = require('./reply'); /** * 根据用户ID,获取未读消息的数量 * Callback: * 回调函数参数列表: * - err, 数据库错误 * - count, 未读消息数量 * @param {String} id 用户ID * @param {Function} callback 获取消息数量 */ exports.getMessagesCount = function (id, callback) { Message.countDocuments({master_id: id, has_read: false}, callback); }; /** * 根据消息Id获取消息 * Callback: * - err, 数据库错误 * - message, 消息对象 * @param {String} id 消息ID * @param {Function} callback 回调函数 */ exports.getMessageById = function (id, callback) { Message.findOne({_id: id}, function (err, message) { if (err) { return callback(err); } getMessageRelations(message, callback); }); }; var getMessageRelations = exports.getMessageRelations = function (message, callback) { if (message.type === 'reply' || message.type === 'reply2' || message.type === 'at') { var proxy = new EventProxy(); proxy.fail(callback); proxy.assign('author', 'topic', 'reply', function (author, topic, reply) { message.author = author; message.topic = topic; message.reply = reply; if (!author || !topic) { message.is_invalid = true; } return callback(null, message); }); // 接收异常 User.getUserById(message.author_id, proxy.done('author')); Topic.getTopicById(message.topic_id, proxy.done('topic')); Reply.getReplyById(message.reply_id, proxy.done('reply')); } else { return callback(null, {is_invalid: true}); } }; /** * 根据用户ID,获取已读消息列表 * Callback: * - err, 数据库异常 * - messages, 消息列表 * @param {String} userId 用户ID * @param {Function} callback 回调函数 */ exports.getReadMessagesByUserId = function (userId, callback) { Message.find({master_id: userId, has_read: true}, null, {sort: '-create_at', limit: 20}, callback); }; /** * 根据用户ID,获取未读消息列表 * Callback: * - err, 数据库异常 * - messages, 未读消息列表 * @param {String} userId 用户ID * @param {Function} callback 回调函数 */ exports.getUnreadMessageByUserId = function (userId, callback) { Message.find({master_id: userId, has_read: false}, null, {sort: '-create_at'}, callback); }; /** * 将消息设置成已读 */ exports.updateMessagesToRead = function (userId, messages, callback) { callback = callback || _.noop; if (messages.length === 0) { return callback(); } var ids = messages.map(function (m) { return m.id; }); var query = { master_id: userId, _id: { $in: ids } }; Message.updateMany(query, { $set: { has_read: true } }).exec(callback); }; /** * 将单个消息设置成已读 */ exports.updateOneMessageToRead = function (msg_id, callback) { callback = callback || _.noop; if (!msg_id) { return callback(); } var query = { _id: msg_id }; Message.updateMany(query, { $set: { has_read: true } }).exec(callback); }; ================================================ FILE: proxy/reply.js ================================================ var models = require('../models'); var Reply = models.Reply; var EventProxy = require('eventproxy'); var tools = require('../common/tools'); var User = require('./user'); var at = require('../common/at'); /** * 获取一条回复信息 * @param {String} id 回复ID * @param {Function} callback 回调函数 */ exports.getReply = function (id, callback) { Reply.findOne({_id: id}, callback); }; /** * 根据回复ID,获取回复 * Callback: * - err, 数据库异常 * - reply, 回复内容 * @param {String} id 回复ID * @param {Function} callback 回调函数 */ exports.getReplyById = function (id, callback) { if (!id) { return callback(null, null); } Reply.findOne({_id: id}, function (err, reply) { if (err) { return callback(err); } if (!reply) { return callback(err, null); } var author_id = reply.author_id; User.getUserById(author_id, function (err, author) { if (err) { return callback(err); } reply.author = author; // TODO: 添加更新方法,有些旧帖子可以转换为markdown格式的内容 if (reply.content_is_html) { return callback(null, reply); } at.linkUsers(reply.content, function (err, str) { if (err) { return callback(err); } reply.content = str; return callback(err, reply); }); }); }); }; /** * 根据主题ID,获取回复列表 * Callback: * - err, 数据库异常 * - replies, 回复列表 * @param {String} id 主题ID * @param {Function} callback 回调函数 */ exports.getRepliesByTopicId = function (id, cb) { Reply.find({topic_id: id, deleted: false}, '', {sort: 'create_at'}, function (err, replies) { if (err) { return cb(err); } if (replies.length === 0) { return cb(null, []); } var proxy = new EventProxy(); proxy.after('reply_find', replies.length, function () { cb(null, replies); }); for (var j = 0; j < replies.length; j++) { (function (i) { var author_id = replies[i].author_id; User.getUserById(author_id, function (err, author) { if (err) { return cb(err); } replies[i].author = author || { _id: '' }; if (replies[i].content_is_html) { return proxy.emit('reply_find'); } at.linkUsers(replies[i].content, function (err, str) { if (err) { return cb(err); } replies[i].content = str; proxy.emit('reply_find'); }); }); })(j); } }); }; /** * 创建并保存一条回复信息 * @param {String} content 回复内容 * @param {String} topicId 主题ID * @param {String} authorId 回复作者 * @param {String} [replyId] 回复ID,当二级回复时设定该值 * @param {Function} callback 回调函数 */ exports.newAndSave = function (content, topicId, authorId, replyId, callback) { if (typeof replyId === 'function') { callback = replyId; replyId = null; } var reply = new Reply(); reply.content = content; reply.topic_id = topicId; reply.author_id = authorId; if (replyId) { reply.reply_id = replyId; } reply.save(function (err) { callback(err, reply); }); }; /** * 根据topicId查询到最新的一条未删除回复 * @param topicId 主题ID * @param callback 回调函数 */ exports.getLastReplyByTopId = function (topicId, callback) { Reply.find({topic_id: topicId, deleted: false}, '_id', {sort: {create_at : -1}, limit : 1}, callback); }; exports.getRepliesByAuthorId = function (authorId, opt, callback) { if (!callback) { callback = opt; opt = null; } Reply.find({author_id: authorId}, {}, opt, callback); }; // 通过 author_id 获取回复总数 exports.getCountByAuthorId = function (authorId, callback) { Reply.countDocuments({author_id: authorId}, callback); }; ================================================ FILE: proxy/topic.js ================================================ var EventProxy = require('eventproxy'); var models = require('../models'); var Topic = models.Topic; var User = require('./user'); var Reply = require('./reply'); var tools = require('../common/tools'); var at = require('../common/at'); var _ = require('lodash'); /** * 根据主题ID获取主题 * Callback: * - err, 数据库错误 * - topic, 主题 * - author, 作者 * - lastReply, 最后回复 * @param {String} id 主题ID * @param {Function} callback 回调函数 */ exports.getTopicById = function (id, callback) { var proxy = new EventProxy(); var events = ['topic', 'author', 'last_reply']; proxy.assign(events, function (topic, author, last_reply) { if (!author) { return callback(null, null, null, null); } return callback(null, topic, author, last_reply); }).fail(callback); Topic.findOne({_id: id}, proxy.done(function (topic) { if (!topic) { proxy.emit('topic', null); proxy.emit('author', null); proxy.emit('last_reply', null); return; } proxy.emit('topic', topic); User.getUserById(topic.author_id, proxy.done('author')); if (topic.last_reply) { Reply.getReplyById(topic.last_reply, proxy.done(function (last_reply) { proxy.emit('last_reply', last_reply); })); } else { proxy.emit('last_reply', null); } })); }; /** * 获取关键词能搜索到的主题数量 * Callback: * - err, 数据库错误 * - count, 主题数量 * @param {String} query 搜索关键词 * @param {Function} callback 回调函数 */ exports.getCountByQuery = function (query, callback) { Topic.countDocuments(query, callback); }; /** * 根据关键词,获取主题列表 * Callback: * - err, 数据库错误 * - count, 主题列表 * @param {String} query 搜索关键词 * @param {Object} opt 搜索选项 * @param {Function} callback 回调函数 */ exports.getTopicsByQuery = function (query, opt, callback) { query.deleted = false; Topic.find(query, {}, opt, function (err, topics) { if (err) { return callback(err); } if (topics.length === 0) { return callback(null, []); } var proxy = new EventProxy(); proxy.after('topic_ready', topics.length, function () { topics = _.compact(topics); // 删除不合规的 topic return callback(null, topics); }); proxy.fail(callback); topics.forEach(function (topic, i) { var ep = new EventProxy(); ep.all('author', 'reply', function (author, reply) { // 保证顺序 // 作者可能已被删除 if (author) { topic.author = author; topic.reply = reply; } else { topics[i] = null; } proxy.emit('topic_ready'); }); User.getUserById(topic.author_id, ep.done('author')); // 获取主题的最后回复 Reply.getReplyById(topic.last_reply, ep.done('reply')); }); }); }; // for sitemap exports.getLimit5w = function (callback) { Topic.find({deleted: false}, '_id', {limit: 50000, sort: '-create_at'}, callback); }; /** * 获取所有信息的主题 * Callback: * - err, 数据库异常 * - message, 消息 * - topic, 主题 * - author, 主题作者 * - replies, 主题的回复 * @param {String} id 主题ID * @param {Function} callback 回调函数 */ exports.getFullTopic = function (id, callback) { var proxy = new EventProxy(); var events = ['topic', 'author', 'replies']; proxy .assign(events, function (topic, author, replies) { callback(null, '', topic, author, replies); }) .fail(callback); Topic.findOne({_id: id, deleted: false}, proxy.done(function (topic) { if (!topic) { proxy.unbind(); return callback(null, '此话题不存在或已被删除。'); } at.linkUsers(topic.content, proxy.done('topic', function (str) { topic.linkedContent = str; return topic; })); User.getUserById(topic.author_id, proxy.done(function (author) { if (!author) { proxy.unbind(); return callback(null, '话题的作者丢了。'); } proxy.emit('author', author); })); Reply.getRepliesByTopicId(topic._id, proxy.done('replies')); })); }; /** * 更新主题的最后回复信息 * @param {String} topicId 主题ID * @param {String} replyId 回复ID * @param {Function} callback 回调函数 */ exports.updateLastReply = function (topicId, replyId, callback) { Topic.findOne({_id: topicId}, function (err, topic) { if (err || !topic) { return callback(err); } topic.last_reply = replyId; topic.last_reply_at = new Date(); topic.reply_count += 1; topic.save(callback); }); }; /** * 根据主题ID,查找一条主题 * @param {String} id 主题ID * @param {Function} callback 回调函数 */ exports.getTopic = function (id, callback) { Topic.findOne({_id: id}, callback); }; /** * 将当前主题的回复计数减1,并且更新最后回复的用户,删除回复时用到 * @param {String} id 主题ID * @param {Function} callback 回调函数 */ exports.reduceCount = function (id, callback) { Topic.findOne({_id: id}, function (err, topic) { if (err) { return callback(err); } if (!topic) { return callback(new Error('该主题不存在')); } topic.reply_count -= 1; Reply.getLastReplyByTopId(id, function (err, reply) { if (err) { return callback(err); } if (reply.length !== 0) { topic.last_reply = reply[0]._id; } else { topic.last_reply = null; } topic.save(callback); }); }); }; exports.newAndSave = function (title, content, tab, authorId, callback) { var topic = new Topic(); topic.title = title; topic.content = content; topic.tab = tab; topic.author_id = authorId; topic.save(callback); }; ================================================ FILE: proxy/topic_collect.js ================================================ var TopicCollect = require('../models').TopicCollect; var _ = require('lodash') exports.getTopicCollect = function (userId, topicId, callback) { TopicCollect.findOne({user_id: userId, topic_id: topicId}, callback); }; exports.getTopicCollectsByUserId = function (userId, opt, callback) { var defaultOpt = {sort: '-create_at'}; opt = _.assign(defaultOpt, opt) TopicCollect.find({user_id: userId}, '', opt, callback); }; exports.newAndSave = function (userId, topicId, callback) { var topic_collect = new TopicCollect(); topic_collect.user_id = userId; topic_collect.topic_id = topicId; topic_collect.save(callback); }; exports.remove = function (userId, topicId, callback) { TopicCollect.deleteOne({user_id: userId, topic_id: topicId}, callback); }; ================================================ FILE: proxy/user.js ================================================ var models = require('../models'); var User = models.User; var utility = require('utility'); var uuid = require('node-uuid'); /** * 根据用户名列表查找用户列表 * Callback: * - err, 数据库异常 * - users, 用户列表 * @param {Array} names 用户名列表 * @param {Function} callback 回调函数 */ exports.getUsersByNames = function (names, callback) { if (names.length === 0) { return callback(null, []); } User.find({ loginname: { $in: names } }, callback); }; /** * 根据登录名查找用户 * Callback: * - err, 数据库异常 * - user, 用户 * @param {String} loginName 登录名 * @param {Function} callback 回调函数 */ exports.getUserByLoginName = function (loginName, callback) { User.findOne({'loginname': new RegExp('^'+loginName+'$', "i")}, callback); }; /** * 根据用户ID,查找用户 * Callback: * - err, 数据库异常 * - user, 用户 * @param {String} id 用户ID * @param {Function} callback 回调函数 */ exports.getUserById = function (id, callback) { if (!id) { return callback(); } User.findOne({_id: id}, callback); }; /** * 根据邮箱,查找用户 * Callback: * - err, 数据库异常 * - user, 用户 * @param {String} email 邮箱地址 * @param {Function} callback 回调函数 */ exports.getUserByMail = function (email, callback) { User.findOne({email: email}, callback); }; /** * 根据用户ID列表,获取一组用户 * Callback: * - err, 数据库异常 * - users, 用户列表 * @param {Array} ids 用户ID列表 * @param {Function} callback 回调函数 */ exports.getUsersByIds = function (ids, callback) { User.find({'_id': {'$in': ids}}, callback); }; /** * 根据关键字,获取一组用户 * Callback: * - err, 数据库异常 * - users, 用户列表 * @param {String} query 关键字 * @param {Object} opt 选项 * @param {Function} callback 回调函数 */ exports.getUsersByQuery = function (query, opt, callback) { User.find(query, '', opt, callback); }; /** * 根据查询条件,获取一个用户 * Callback: * - err, 数据库异常 * - user, 用户 * @param {String} name 用户名 * @param {String} key 激活码 * @param {Function} callback 回调函数 */ exports.getUserByNameAndKey = function (loginname, key, callback) { User.findOne({loginname: loginname, retrieve_key: key}, callback); }; exports.newAndSave = function (name, loginname, pass, email, avatar_url, active, callback) { var user = new User(); user.name = loginname; user.loginname = loginname; user.pass = pass; user.email = email; user.avatar = avatar_url; user.active = active || false; user.accessToken = uuid.v4(); user.save(callback); }; var makeGravatar = function (email) { return 'http://www.gravatar.com/avatar/' + utility.md5(email.toLowerCase()) + '?size=48'; }; exports.makeGravatar = makeGravatar; exports.getGravatar = function (user) { return user.avatar || makeGravatar(user); }; ================================================ FILE: public/github-card.html ================================================ ================================================ FILE: public/javascripts/main.js ================================================ $(document).ready(function () { var windowHeight = $(window).height(); var $backtotop = $('#backtotop'); var top = windowHeight - $backtotop.height() - 200; function moveBacktotop() { $backtotop.css({ top: top, right: 0}); } function footerFixBottom() { if($(document.body).height() < windowHeight){ $("#footer").addClass('fix-bottom'); }else{ $("#footer").removeClass('fix-bottom'); } } $backtotop.click(function () { $('html,body').animate({ scrollTop: 0 }); return false; }); $(window).scroll(function () { var windowHeight = $(window).scrollTop(); if (windowHeight > 200) { $backtotop.fadeIn(); } else { $backtotop.fadeOut(); } }); moveBacktotop(); footerFixBottom(); $(window).resize(moveBacktotop); $(window).resize(footerFixBottom); $('.topic_content a,.reply_content a').attr('target', '_blank'); // pretty code prettyPrint(); // data-loading-text="提交中" $('.submit_btn').click(function () { $(this).button('loading'); }); // 广告的统计信息 $('.sponsor_outlink').click(function () { var $this = $(this); var label = $this.data('label'); ga('send', 'event', 'banner', 'click', label, 1.00, {'nonInteraction': 1}); }); }); ================================================ FILE: public/javascripts/responsive.js ================================================ $(document).ready(function () { var $responsiveBtn = $('#responsive-sidebar-trigger'), $sidebarMask = $('#sidebar-mask'), $sidebar = $('#sidebar'), $main = $('#main'), winWidth = $(window).width(), startX = 0, startY = 0, delta = { x: 0, y: 0 }, swipeThreshold = winWidth / 3, toggleSideBar = function () { var isShow = $responsiveBtn.data('is-show'), mainHeight = $main.height(), sidebarHeight = $sidebar.outerHeight(); $sidebar.css({right: isShow ? -300 : 0}); $responsiveBtn.data('is-show', !isShow); if (!isShow && mainHeight < sidebarHeight) { $main.height(sidebarHeight); } $sidebarMask[isShow ? 'fadeOut' : 'fadeIn']().height($('body').height()); $sidebar[isShow ? 'hide' : 'show']() }, touchstart = function (e) { var touchs = e.targetTouches; startX = +touchs[0].pageX; startY = +touchs[0].pageY; delta.x = delta.y = 0; document.body.addEventListener('touchmove', touchmove, false); document.body.addEventListener('touchend', touchend, false); }, touchmove = function (e) { var touchs = e.changedTouches; delta.x = +touchs[0].pageX - startX; delta.y = +touchs[0].pageY - startY; //当水平距离大于垂直距离时,才认为是用户想滑动打开右侧栏 if (Math.abs(delta.x) > Math.abs(delta.y)) { e.preventDefault(); } }, touchend = function (e) { var touchs = e.changedTouches, isShow = $responsiveBtn.data('is-show'); delta.x = +touchs[0].pageX - startX; //右侧栏未显示&&用户touch点在屏幕右侧1/4区域内&&move距离大于阀值时,打开右侧栏 if (!isShow && (startX > winWidth * 3 / 4) && Math.abs(delta.x) > swipeThreshold) { $responsiveBtn.trigger('click'); } //右侧栏显示中&&用户touch点在屏幕左侧侧1/4区域内&&move距离大于阀值时,关闭右侧栏 if (isShow && (startX < winWidth * 1 / 4) && Math.abs(delta.x) > swipeThreshold) { $responsiveBtn.trigger('click'); } startX = startY = 0; delta.x = delta.y = 0; document.body.removeEventListener('touchmove', touchmove, false); document.body.removeEventListener('touchend', touchend, false); }; if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { document.body.addEventListener('touchstart', touchstart); } $responsiveBtn.on('click', toggleSideBar); $sidebarMask.on('click', function () { $responsiveBtn.trigger('click'); }); }); ================================================ FILE: public/libs/bootstrap/css/bootstrap-responsive.css ================================================ /*! * Bootstrap Responsive v2.3.1 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */ .clearfix { *zoom: 1; } .clearfix:before, .clearfix:after { display: table; line-height: 0; content: ""; } .clearfix:after { clear: both; } .hide-text { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .input-block-level { display: block; width: 100%; min-height: 30px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } @-ms-viewport { width: device-width; } .hidden { display: none; visibility: hidden; } .visible-phone { display: none !important; } .visible-tablet { display: none !important; } .hidden-desktop { display: none !important; } .visible-desktop { display: inherit !important; } @media (min-width: 768px) and (max-width: 979px) { .hidden-desktop { display: inherit !important; } .visible-desktop { display: none !important; } .visible-tablet { display: inherit !important; } .hidden-tablet { display: none !important; } } @media (max-width: 767px) { .hidden-desktop { display: inherit !important; } .visible-desktop { display: none !important; } .visible-phone { display: inherit !important; } .hidden-phone { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: inherit !important; } .hidden-print { display: none !important; } } @media (min-width: 1200px) { .row { margin-left: -30px; *zoom: 1; } .row:before, .row:after { display: table; line-height: 0; content: ""; } .row:after { clear: both; } [class*="span"] { float: left; min-height: 1px; margin-left: 30px; } .container, .navbar-static-top .container, .navbar-fixed-top .container, .navbar-fixed-bottom .container { width: 1170px; } .span12 { width: 1170px; } .span11 { width: 1070px; } .span10 { width: 970px; } .span9 { width: 870px; } .span8 { width: 770px; } .span7 { width: 670px; } .span6 { width: 570px; } .span5 { width: 470px; } .span4 { width: 370px; } .span3 { width: 270px; } .span2 { width: 170px; } .span1 { width: 70px; } .offset12 { margin-left: 1230px; } .offset11 { margin-left: 1130px; } .offset10 { margin-left: 1030px; } .offset9 { margin-left: 930px; } .offset8 { margin-left: 830px; } .offset7 { margin-left: 730px; } .offset6 { margin-left: 630px; } .offset5 { margin-left: 530px; } .offset4 { margin-left: 430px; } .offset3 { margin-left: 330px; } .offset2 { margin-left: 230px; } .offset1 { margin-left: 130px; } .row-fluid { width: 100%; *zoom: 1; } .row-fluid:before, .row-fluid:after { display: table; line-height: 0; content: ""; } .row-fluid:after { clear: both; } .row-fluid [class*="span"] { display: block; float: left; width: 100%; min-height: 30px; margin-left: 2.564102564102564%; *margin-left: 2.5109110747408616%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .row-fluid [class*="span"]:first-child { margin-left: 0; } .row-fluid .controls-row [class*="span"] + [class*="span"] { margin-left: 2.564102564102564%; } .row-fluid .span12 { width: 100%; *width: 99.94680851063829%; } .row-fluid .span11 { width: 91.45299145299145%; *width: 91.39979996362975%; } .row-fluid .span10 { width: 82.90598290598291%; *width: 82.8527914166212%; } .row-fluid .span9 { width: 74.35897435897436%; *width: 74.30578286961266%; } .row-fluid .span8 { width: 65.81196581196582%; *width: 65.75877432260411%; } .row-fluid .span7 { width: 57.26495726495726%; *width: 57.21176577559556%; } .row-fluid .span6 { width: 48.717948717948715%; *width: 48.664757228587014%; } .row-fluid .span5 { width: 40.17094017094017%; *width: 40.11774868157847%; } .row-fluid .span4 { width: 31.623931623931625%; *width: 31.570740134569924%; } .row-fluid .span3 { width: 23.076923076923077%; *width: 23.023731587561375%; } .row-fluid .span2 { width: 14.52991452991453%; *width: 14.476723040552828%; } .row-fluid .span1 { width: 5.982905982905983%; *width: 5.929714493544281%; } .row-fluid .offset12 { margin-left: 105.12820512820512%; *margin-left: 105.02182214948171%; } .row-fluid .offset12:first-child { margin-left: 102.56410256410257%; *margin-left: 102.45771958537915%; } .row-fluid .offset11 { margin-left: 96.58119658119658%; *margin-left: 96.47481360247316%; } .row-fluid .offset11:first-child { margin-left: 94.01709401709402%; *margin-left: 93.91071103837061%; } .row-fluid .offset10 { margin-left: 88.03418803418803%; *margin-left: 87.92780505546462%; } .row-fluid .offset10:first-child { margin-left: 85.47008547008548%; *margin-left: 85.36370249136206%; } .row-fluid .offset9 { margin-left: 79.48717948717949%; *margin-left: 79.38079650845607%; } .row-fluid .offset9:first-child { margin-left: 76.92307692307693%; *margin-left: 76.81669394435352%; } .row-fluid .offset8 { margin-left: 70.94017094017094%; *margin-left: 70.83378796144753%; } .row-fluid .offset8:first-child { margin-left: 68.37606837606839%; *margin-left: 68.26968539734497%; } .row-fluid .offset7 { margin-left: 62.393162393162385%; *margin-left: 62.28677941443899%; } .row-fluid .offset7:first-child { margin-left: 59.82905982905982%; *margin-left: 59.72267685033642%; } .row-fluid .offset6 { margin-left: 53.84615384615384%; *margin-left: 53.739770867430444%; } .row-fluid .offset6:first-child { margin-left: 51.28205128205128%; *margin-left: 51.175668303327875%; } .row-fluid .offset5 { margin-left: 45.299145299145295%; *margin-left: 45.1927623204219%; } .row-fluid .offset5:first-child { margin-left: 42.73504273504273%; *margin-left: 42.62865975631933%; } .row-fluid .offset4 { margin-left: 36.75213675213675%; *margin-left: 36.645753773413354%; } .row-fluid .offset4:first-child { margin-left: 34.18803418803419%; *margin-left: 34.081651209310785%; } .row-fluid .offset3 { margin-left: 28.205128205128204%; *margin-left: 28.0987452264048%; } .row-fluid .offset3:first-child { margin-left: 25.641025641025642%; *margin-left: 25.53464266230224%; } .row-fluid .offset2 { margin-left: 19.65811965811966%; *margin-left: 19.551736679396257%; } .row-fluid .offset2:first-child { margin-left: 17.094017094017094%; *margin-left: 16.98763411529369%; } .row-fluid .offset1 { margin-left: 11.11111111111111%; *margin-left: 11.004728132387708%; } .row-fluid .offset1:first-child { margin-left: 8.547008547008547%; *margin-left: 8.440625568285142%; } input, textarea, .uneditable-input { margin-left: 0; } .controls-row [class*="span"] + [class*="span"] { margin-left: 30px; } input.span12, textarea.span12, .uneditable-input.span12 { width: 1156px; } input.span11, textarea.span11, .uneditable-input.span11 { width: 1056px; } input.span10, textarea.span10, .uneditable-input.span10 { width: 956px; } input.span9, textarea.span9, .uneditable-input.span9 { width: 856px; } input.span8, textarea.span8, .uneditable-input.span8 { width: 756px; } input.span7, textarea.span7, .uneditable-input.span7 { width: 656px; } input.span6, textarea.span6, .uneditable-input.span6 { width: 556px; } input.span5, textarea.span5, .uneditable-input.span5 { width: 456px; } input.span4, textarea.span4, .uneditable-input.span4 { width: 356px; } input.span3, textarea.span3, .uneditable-input.span3 { width: 256px; } input.span2, textarea.span2, .uneditable-input.span2 { width: 156px; } input.span1, textarea.span1, .uneditable-input.span1 { width: 56px; } .thumbnails { margin-left: -30px; } .thumbnails > li { margin-left: 30px; } .row-fluid .thumbnails { margin-left: 0; } } @media (min-width: 768px) and (max-width: 979px) { .row { margin-left: -20px; *zoom: 1; } .row:before, .row:after { display: table; line-height: 0; content: ""; } .row:after { clear: both; } [class*="span"] { float: left; min-height: 1px; margin-left: 20px; } .container, .navbar-static-top .container, .navbar-fixed-top .container, .navbar-fixed-bottom .container { width: 724px; } .span12 { width: 724px; } .span11 { width: 662px; } .span10 { width: 600px; } .span9 { width: 538px; } .span8 { width: 476px; } .span7 { width: 414px; } .span6 { width: 352px; } .span5 { width: 290px; } .span4 { width: 228px; } .span3 { width: 166px; } .span2 { width: 104px; } .span1 { width: 42px; } .offset12 { margin-left: 764px; } .offset11 { margin-left: 702px; } .offset10 { margin-left: 640px; } .offset9 { margin-left: 578px; } .offset8 { margin-left: 516px; } .offset7 { margin-left: 454px; } .offset6 { margin-left: 392px; } .offset5 { margin-left: 330px; } .offset4 { margin-left: 268px; } .offset3 { margin-left: 206px; } .offset2 { margin-left: 144px; } .offset1 { margin-left: 82px; } .row-fluid { width: 100%; *zoom: 1; } .row-fluid:before, .row-fluid:after { display: table; line-height: 0; content: ""; } .row-fluid:after { clear: both; } .row-fluid [class*="span"] { display: block; float: left; width: 100%; min-height: 30px; margin-left: 2.7624309392265194%; *margin-left: 2.709239449864817%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .row-fluid [class*="span"]:first-child { margin-left: 0; } .row-fluid .controls-row [class*="span"] + [class*="span"] { margin-left: 2.7624309392265194%; } .row-fluid .span12 { width: 100%; *width: 99.94680851063829%; } .row-fluid .span11 { width: 91.43646408839778%; *width: 91.38327259903608%; } .row-fluid .span10 { width: 82.87292817679558%; *width: 82.81973668743387%; } .row-fluid .span9 { width: 74.30939226519337%; *width: 74.25620077583166%; } .row-fluid .span8 { width: 65.74585635359117%; *width: 65.69266486422946%; } .row-fluid .span7 { width: 57.18232044198895%; *width: 57.12912895262725%; } .row-fluid .span6 { width: 48.61878453038674%; *width: 48.56559304102504%; } .row-fluid .span5 { width: 40.05524861878453%; *width: 40.00205712942283%; } .row-fluid .span4 { width: 31.491712707182323%; *width: 31.43852121782062%; } .row-fluid .span3 { width: 22.92817679558011%; *width: 22.87498530621841%; } .row-fluid .span2 { width: 14.3646408839779%; *width: 14.311449394616199%; } .row-fluid .span1 { width: 5.801104972375691%; *width: 5.747913483013988%; } .row-fluid .offset12 { margin-left: 105.52486187845304%; *margin-left: 105.41847889972962%; } .row-fluid .offset12:first-child { margin-left: 102.76243093922652%; *margin-left: 102.6560479605031%; } .row-fluid .offset11 { margin-left: 96.96132596685082%; *margin-left: 96.8549429881274%; } .row-fluid .offset11:first-child { margin-left: 94.1988950276243%; *margin-left: 94.09251204890089%; } .row-fluid .offset10 { margin-left: 88.39779005524862%; *margin-left: 88.2914070765252%; } .row-fluid .offset10:first-child { margin-left: 85.6353591160221%; *margin-left: 85.52897613729868%; } .row-fluid .offset9 { margin-left: 79.8342541436464%; *margin-left: 79.72787116492299%; } .row-fluid .offset9:first-child { margin-left: 77.07182320441989%; *margin-left: 76.96544022569647%; } .row-fluid .offset8 { margin-left: 71.2707182320442%; *margin-left: 71.16433525332079%; } .row-fluid .offset8:first-child { margin-left: 68.50828729281768%; *margin-left: 68.40190431409427%; } .row-fluid .offset7 { margin-left: 62.70718232044199%; *margin-left: 62.600799341718584%; } .row-fluid .offset7:first-child { margin-left: 59.94475138121547%; *margin-left: 59.838368402492065%; } .row-fluid .offset6 { margin-left: 54.14364640883978%; *margin-left: 54.037263430116376%; } .row-fluid .offset6:first-child { margin-left: 51.38121546961326%; *margin-left: 51.27483249088986%; } .row-fluid .offset5 { margin-left: 45.58011049723757%; *margin-left: 45.47372751851417%; } .row-fluid .offset5:first-child { margin-left: 42.81767955801105%; *margin-left: 42.71129657928765%; } .row-fluid .offset4 { margin-left: 37.01657458563536%; *margin-left: 36.91019160691196%; } .row-fluid .offset4:first-child { margin-left: 34.25414364640884%; *margin-left: 34.14776066768544%; } .row-fluid .offset3 { margin-left: 28.45303867403315%; *margin-left: 28.346655695309746%; } .row-fluid .offset3:first-child { margin-left: 25.69060773480663%; *margin-left: 25.584224756083227%; } .row-fluid .offset2 { margin-left: 19.88950276243094%; *margin-left: 19.783119783707537%; } .row-fluid .offset2:first-child { margin-left: 17.12707182320442%; *margin-left: 17.02068884448102%; } .row-fluid .offset1 { margin-left: 11.32596685082873%; *margin-left: 11.219583872105325%; } .row-fluid .offset1:first-child { margin-left: 8.56353591160221%; *margin-left: 8.457152932878806%; } input, textarea, .uneditable-input { margin-left: 0; } .controls-row [class*="span"] + [class*="span"] { margin-left: 20px; } input.span12, textarea.span12, .uneditable-input.span12 { width: 710px; } input.span11, textarea.span11, .uneditable-input.span11 { width: 648px; } input.span10, textarea.span10, .uneditable-input.span10 { width: 586px; } input.span9, textarea.span9, .uneditable-input.span9 { width: 524px; } input.span8, textarea.span8, .uneditable-input.span8 { width: 462px; } input.span7, textarea.span7, .uneditable-input.span7 { width: 400px; } input.span6, textarea.span6, .uneditable-input.span6 { width: 338px; } input.span5, textarea.span5, .uneditable-input.span5 { width: 276px; } input.span4, textarea.span4, .uneditable-input.span4 { width: 214px; } input.span3, textarea.span3, .uneditable-input.span3 { width: 152px; } input.span2, textarea.span2, .uneditable-input.span2 { width: 90px; } input.span1, textarea.span1, .uneditable-input.span1 { width: 28px; } } @media (max-width: 767px) { body { padding-right: 20px; padding-left: 20px; } .navbar-fixed-top, .navbar-fixed-bottom, .navbar-static-top { margin-right: -20px; margin-left: -20px; } .container-fluid { padding: 0; } .dl-horizontal dt { float: none; width: auto; clear: none; text-align: left; } .dl-horizontal dd { margin-left: 0; } .container { width: auto; } .row-fluid { width: 100%; } .row, .thumbnails { margin-left: 0; } .thumbnails > li { float: none; margin-left: 0; } [class*="span"], .uneditable-input[class*="span"], .row-fluid [class*="span"] { display: block; float: none; width: 100%; margin-left: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .span12, .row-fluid .span12 { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .row-fluid [class*="offset"]:first-child { margin-left: 0; } .input-large, .input-xlarge, .input-xxlarge, input[class*="span"], select[class*="span"], textarea[class*="span"], .uneditable-input { display: block; width: 100%; min-height: 30px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .input-prepend input, .input-append input, .input-prepend input[class*="span"], .input-append input[class*="span"] { display: inline-block; width: auto; } .controls-row [class*="span"] + [class*="span"] { margin-left: 0; } .modal { position: fixed; top: 20px; right: 20px; left: 20px; width: auto; margin: 0; } .modal.fade { top: -100px; } .modal.fade.in { top: 20px; } } @media (max-width: 480px) { .nav-collapse { -webkit-transform: translate3d(0, 0, 0); } .page-header h1 small { display: block; line-height: 20px; } input[type="checkbox"], input[type="radio"] { border: 1px solid #ccc; } .form-horizontal .control-label { float: none; width: auto; padding-top: 0; text-align: left; } .form-horizontal .controls { margin-left: 0; } .form-horizontal .control-list { padding-top: 0; } .form-horizontal .form-actions { padding-right: 10px; padding-left: 10px; } .media .pull-left, .media .pull-right { display: block; float: none; margin-bottom: 10px; } .media-object { margin-right: 0; margin-left: 0; } .modal { top: 10px; right: 10px; left: 10px; } .modal-header .close { padding: 10px; margin: -10px; } .carousel-caption { position: static; } } @media (max-width: 979px) { body { padding-top: 0; } .navbar-fixed-top, .navbar-fixed-bottom { position: static; } .navbar-fixed-top { margin-bottom: 20px; } .navbar-fixed-bottom { margin-top: 20px; } .navbar-fixed-top .navbar-inner, .navbar-fixed-bottom .navbar-inner { padding: 5px; } .navbar .container { width: auto; padding: 0; } .navbar .brand { padding-right: 10px; padding-left: 10px; margin: 0 0 0 -5px; } .nav-collapse { clear: both; } .nav-collapse .nav { float: none; margin: 0 0 10px; } .nav-collapse .nav > li { float: none; } .nav-collapse .nav > li > a { margin-bottom: 2px; } .nav-collapse .nav > .divider-vertical { display: none; } .nav-collapse .nav .nav-header { color: #777777; text-shadow: none; } .nav-collapse .nav > li > a, .nav-collapse .dropdown-menu a { padding: 9px 15px; font-weight: bold; color: #777777; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .nav-collapse .btn { padding: 4px 10px 4px; font-weight: normal; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .nav-collapse .dropdown-menu li + li a { margin-bottom: 2px; } .nav-collapse .nav > li > a:hover, .nav-collapse .nav > li > a:focus, .nav-collapse .dropdown-menu a:hover, .nav-collapse .dropdown-menu a:focus { background-color: #f2f2f2; } .navbar-inverse .nav-collapse .nav > li > a, .navbar-inverse .nav-collapse .dropdown-menu a { color: #999999; } .navbar-inverse .nav-collapse .nav > li > a:hover, .navbar-inverse .nav-collapse .nav > li > a:focus, .navbar-inverse .nav-collapse .dropdown-menu a:hover, .navbar-inverse .nav-collapse .dropdown-menu a:focus { background-color: #111111; } .nav-collapse.in .btn-group { padding: 0; margin-top: 5px; } .nav-collapse .dropdown-menu { position: static; top: auto; left: auto; display: none; float: none; max-width: none; padding: 0; margin: 0 15px; background-color: transparent; border: none; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } .nav-collapse .open > .dropdown-menu { display: block; } .nav-collapse .dropdown-menu:before, .nav-collapse .dropdown-menu:after { display: none; } .nav-collapse .dropdown-menu .divider { display: none; } .nav-collapse .nav > li > .dropdown-menu:before, .nav-collapse .nav > li > .dropdown-menu:after { display: none; } .nav-collapse .navbar-form, .nav-collapse .navbar-search { float: none; padding: 10px 15px; margin: 10px 0; border-top: 1px solid #f2f2f2; border-bottom: 1px solid #f2f2f2; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); } .navbar-inverse .nav-collapse .navbar-form, .navbar-inverse .nav-collapse .navbar-search { border-top-color: #111111; border-bottom-color: #111111; } .navbar .nav-collapse .nav.pull-right { float: none; margin-left: 0; } .nav-collapse, .nav-collapse.collapse { height: 0; overflow: hidden; } .navbar .btn-navbar { display: block; } .navbar-static .navbar-inner { padding-right: 10px; padding-left: 10px; } } @media (min-width: 980px) { .nav-collapse.collapse { height: auto !important; overflow: visible !important; } } ================================================ FILE: public/libs/bootstrap/css/bootstrap.css ================================================ /*! * Bootstrap v2.3.1 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */ .clearfix { *zoom: 1; } .clearfix:before, .clearfix:after { display: table; line-height: 0; content: ""; } .clearfix:after { clear: both; } .hide-text { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .input-block-level { display: block; width: 100%; min-height: 30px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } audio:not([controls]) { display: none; } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } a:hover, a:active { outline: 0; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { width: auto\9; height: auto; max-width: 100%; vertical-align: middle; border: 0; -ms-interpolation-mode: bicubic; } #map_canvas img, .google-maps img { max-width: none; } button, input, select, textarea { margin: 0; font-size: 100%; vertical-align: middle; } button, input { *overflow: visible; line-height: normal; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } button, html input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } label, select, button, input[type="button"], input[type="reset"], input[type="submit"], input[type="radio"], input[type="checkbox"] { cursor: pointer; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; } textarea { overflow: auto; vertical-align: top; } @media print { * { color: #000 !important; text-shadow: none !important; background: transparent !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 0.5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } } body { margin: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; color: #333333; background-color: #ffffff; } a { color: #0088cc; text-decoration: none; } a:hover, a:focus { color: #005580; text-decoration: underline; } .img-rounded { -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .img-polaroid { padding: 4px; background-color: #fff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .img-circle { -webkit-border-radius: 500px; -moz-border-radius: 500px; border-radius: 500px; } .row { margin-left: -20px; *zoom: 1; } .row:before, .row:after { display: table; line-height: 0; content: ""; } .row:after { clear: both; } [class*="span"] { float: left; min-height: 1px; margin-left: 20px; } .container, .navbar-static-top .container, .navbar-fixed-top .container, .navbar-fixed-bottom .container { width: 940px; } .span12 { width: 940px; } .span11 { width: 860px; } .span10 { width: 780px; } .span9 { width: 700px; } .span8 { width: 620px; } .span7 { width: 540px; } .span6 { width: 460px; } .span5 { width: 380px; } .span4 { width: 300px; } .span3 { width: 220px; } .span2 { width: 140px; } .span1 { width: 60px; } .offset12 { margin-left: 980px; } .offset11 { margin-left: 900px; } .offset10 { margin-left: 820px; } .offset9 { margin-left: 740px; } .offset8 { margin-left: 660px; } .offset7 { margin-left: 580px; } .offset6 { margin-left: 500px; } .offset5 { margin-left: 420px; } .offset4 { margin-left: 340px; } .offset3 { margin-left: 260px; } .offset2 { margin-left: 180px; } .offset1 { margin-left: 100px; } .row-fluid { width: 100%; *zoom: 1; } .row-fluid:before, .row-fluid:after { display: table; line-height: 0; content: ""; } .row-fluid:after { clear: both; } .row-fluid [class*="span"] { display: block; float: left; width: 100%; min-height: 30px; margin-left: 2.127659574468085%; *margin-left: 2.074468085106383%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .row-fluid [class*="span"]:first-child { margin-left: 0; } .row-fluid .controls-row [class*="span"] + [class*="span"] { margin-left: 2.127659574468085%; } .row-fluid .span12 { width: 100%; *width: 99.94680851063829%; } .row-fluid .span11 { width: 91.48936170212765%; *width: 91.43617021276594%; } .row-fluid .span10 { width: 82.97872340425532%; *width: 82.92553191489361%; } .row-fluid .span9 { width: 74.46808510638297%; *width: 74.41489361702126%; } .row-fluid .span8 { width: 65.95744680851064%; *width: 65.90425531914893%; } .row-fluid .span7 { width: 57.44680851063829%; *width: 57.39361702127659%; } .row-fluid .span6 { width: 48.93617021276595%; *width: 48.88297872340425%; } .row-fluid .span5 { width: 40.42553191489362%; *width: 40.37234042553192%; } .row-fluid .span4 { width: 31.914893617021278%; *width: 31.861702127659576%; } .row-fluid .span3 { width: 23.404255319148934%; *width: 23.351063829787233%; } .row-fluid .span2 { width: 14.893617021276595%; *width: 14.840425531914894%; } .row-fluid .span1 { width: 6.382978723404255%; *width: 6.329787234042553%; } .row-fluid .offset12 { margin-left: 104.25531914893617%; *margin-left: 104.14893617021275%; } .row-fluid .offset12:first-child { margin-left: 102.12765957446808%; *margin-left: 102.02127659574467%; } .row-fluid .offset11 { margin-left: 95.74468085106382%; *margin-left: 95.6382978723404%; } .row-fluid .offset11:first-child { margin-left: 93.61702127659574%; *margin-left: 93.51063829787232%; } .row-fluid .offset10 { margin-left: 87.23404255319149%; *margin-left: 87.12765957446807%; } .row-fluid .offset10:first-child { margin-left: 85.1063829787234%; *margin-left: 84.99999999999999%; } .row-fluid .offset9 { margin-left: 78.72340425531914%; *margin-left: 78.61702127659572%; } .row-fluid .offset9:first-child { margin-left: 76.59574468085106%; *margin-left: 76.48936170212764%; } .row-fluid .offset8 { margin-left: 70.2127659574468%; *margin-left: 70.10638297872339%; } .row-fluid .offset8:first-child { margin-left: 68.08510638297872%; *margin-left: 67.9787234042553%; } .row-fluid .offset7 { margin-left: 61.70212765957446%; *margin-left: 61.59574468085106%; } .row-fluid .offset7:first-child { margin-left: 59.574468085106375%; *margin-left: 59.46808510638297%; } .row-fluid .offset6 { margin-left: 53.191489361702125%; *margin-left: 53.085106382978715%; } .row-fluid .offset6:first-child { margin-left: 51.063829787234035%; *margin-left: 50.95744680851063%; } .row-fluid .offset5 { margin-left: 44.68085106382979%; *margin-left: 44.57446808510638%; } .row-fluid .offset5:first-child { margin-left: 42.5531914893617%; *margin-left: 42.4468085106383%; } .row-fluid .offset4 { margin-left: 36.170212765957444%; *margin-left: 36.06382978723405%; } .row-fluid .offset4:first-child { margin-left: 34.04255319148936%; *margin-left: 33.93617021276596%; } .row-fluid .offset3 { margin-left: 27.659574468085104%; *margin-left: 27.5531914893617%; } .row-fluid .offset3:first-child { margin-left: 25.53191489361702%; *margin-left: 25.425531914893618%; } .row-fluid .offset2 { margin-left: 19.148936170212764%; *margin-left: 19.04255319148936%; } .row-fluid .offset2:first-child { margin-left: 17.02127659574468%; *margin-left: 16.914893617021278%; } .row-fluid .offset1 { margin-left: 10.638297872340425%; *margin-left: 10.53191489361702%; } .row-fluid .offset1:first-child { margin-left: 8.51063829787234%; *margin-left: 8.404255319148938%; } [class*="span"].hide, .row-fluid [class*="span"].hide { display: none; } [class*="span"].pull-right, .row-fluid [class*="span"].pull-right { float: right; } .container { margin-right: auto; margin-left: auto; *zoom: 1; } .container:before, .container:after { display: table; line-height: 0; content: ""; } .container:after { clear: both; } .container-fluid { padding-right: 20px; padding-left: 20px; *zoom: 1; } .container-fluid:before, .container-fluid:after { display: table; line-height: 0; content: ""; } .container-fluid:after { clear: both; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 21px; font-weight: 200; line-height: 30px; } small { font-size: 85%; } strong { font-weight: bold; } em { font-style: italic; } cite { font-style: normal; } .muted { color: #999999; } a.muted:hover, a.muted:focus { color: #808080; } .text-warning { color: #c09853; } a.text-warning:hover, a.text-warning:focus { color: #a47e3c; } .text-error { color: #b94a48; } a.text-error:hover, a.text-error:focus { color: #953b39; } .text-info { color: #3a87ad; } a.text-info:hover, a.text-info:focus { color: #2d6987; } .text-success { color: #468847; } a.text-success:hover, a.text-success:focus { color: #356635; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } h1, h2, h3, h4, h5, h6 { margin: 10px 0; font-family: inherit; font-weight: bold; line-height: 20px; color: inherit; text-rendering: optimizelegibility; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-weight: normal; line-height: 1; color: #999999; } h1, h2, h3 { line-height: 40px; } h1 { font-size: 38.5px; } h2 { font-size: 31.5px; } h3 { font-size: 24.5px; } h4 { font-size: 17.5px; } h5 { font-size: 14px; } h6 { font-size: 11.9px; } h1 small { font-size: 24.5px; } h2 small { font-size: 17.5px; } h3 small { font-size: 14px; } h4 small { font-size: 14px; } .page-header { padding-bottom: 9px; margin: 20px 0 30px; border-bottom: 1px solid #eeeeee; } ul, ol { padding: 0; margin: 0 0 10px 25px; } ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; } li { line-height: 20px; } ul.unstyled, ol.unstyled { margin-left: 0; list-style: none; } ul.inline, ol.inline { margin-left: 0; list-style: none; } ul.inline > li, ol.inline > li { display: inline-block; *display: inline; padding-right: 5px; padding-left: 5px; *zoom: 1; } dl { margin-bottom: 20px; } dt, dd { line-height: 20px; } dt { font-weight: bold; } dd { margin-left: 10px; } .dl-horizontal { *zoom: 1; } .dl-horizontal:before, .dl-horizontal:after { display: table; line-height: 0; content: ""; } .dl-horizontal:after { clear: both; } .dl-horizontal dt { float: left; width: 160px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } hr { margin: 20px 0; border: 0; border-top: 1px solid #eeeeee; border-bottom: 1px solid #ffffff; } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #999999; } abbr.initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 0 0 0 15px; margin: 0 0 20px; border-left: 5px solid #eeeeee; } blockquote p { margin-bottom: 0; font-size: 17.5px; font-weight: 300; line-height: 1.25; } blockquote small { display: block; line-height: 20px; color: #999999; } blockquote small:before { content: '\2014 \00A0'; } blockquote.pull-right { float: right; padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; } blockquote.pull-right p, blockquote.pull-right small { text-align: right; } blockquote.pull-right small:before { content: ''; } blockquote.pull-right small:after { content: '\00A0 \2014'; } q:before, q:after, blockquote:before, blockquote:after { content: ""; } address { display: block; margin-bottom: 20px; font-style: normal; line-height: 20px; } code, pre { padding: 0 3px 2px; font-family: Monaco, Menlo, Consolas, "Courier New", monospace; font-size: 12px; color: #333333; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } code { padding: 2px 4px; color: #d14; white-space: nowrap; background-color: #f7f7f9; border: 1px solid #e1e1e8; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre; white-space: pre-wrap; background-color: #f5f5f5; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.15); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } pre.prettyprint { margin-bottom: 20px; } pre code { padding: 0; color: inherit; white-space: pre; white-space: pre-wrap; background-color: transparent; border: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } form { margin: 0 0 20px; } fieldset { padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: 40px; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } legend small { font-size: 15px; color: #999999; } label, input, button, select, textarea { font-size: 14px; font-weight: normal; line-height: 20px; } input, button, select, textarea { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } label { display: block; margin-bottom: 5px; } select, textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"], .uneditable-input { display: inline-block; height: 20px; padding: 4px 6px; margin-bottom: 10px; font-size: 14px; line-height: 20px; color: #555555; vertical-align: middle; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } input, textarea, .uneditable-input { width: 206px; } textarea { height: auto; } textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"], .uneditable-input { background-color: #ffffff; border: 1px solid #cccccc; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; -moz-transition: border linear 0.2s, box-shadow linear 0.2s; -o-transition: border linear 0.2s, box-shadow linear 0.2s; transition: border linear 0.2s, box-shadow linear 0.2s; } textarea:focus, input[type="text"]:focus, input[type="password"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="date"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, input[type="number"]:focus, input[type="email"]:focus, input[type="url"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus, .uneditable-input:focus { border-color: rgba(82, 168, 236, 0.8); outline: 0; outline: thin dotted \9; /* IE6-9 */ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; *margin-top: 0; line-height: normal; } input[type="file"], input[type="image"], input[type="submit"], input[type="reset"], input[type="button"], input[type="radio"], input[type="checkbox"] { width: auto; } select, input[type="file"] { height: 30px; /* In IE7, the height of the select element cannot be changed by height, only font-size */ *margin-top: 4px; /* For IE7, add top margin to align select with labels */ line-height: 30px; } select { width: 220px; background-color: #ffffff; border: 1px solid #cccccc; } select[multiple], select[size] { height: auto; } select:focus, input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .uneditable-input, .uneditable-textarea { color: #999999; cursor: not-allowed; background-color: #fcfcfc; border-color: #cccccc; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); } .uneditable-input { overflow: hidden; white-space: nowrap; } .uneditable-textarea { width: auto; height: auto; } input:-moz-placeholder, textarea:-moz-placeholder { color: #999999; } input:-ms-input-placeholder, textarea:-ms-input-placeholder { color: #999999; } input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: #999999; } .radio, .checkbox { min-height: 20px; padding-left: 20px; } .radio input[type="radio"], .checkbox input[type="checkbox"] { float: left; margin-left: -20px; } .controls > .radio:first-child, .controls > .checkbox:first-child { padding-top: 5px; } .radio.inline, .checkbox.inline { display: inline-block; padding-top: 5px; margin-bottom: 0; vertical-align: middle; } .radio.inline + .radio.inline, .checkbox.inline + .checkbox.inline { margin-left: 10px; } .input-mini { width: 60px; } .input-small { width: 90px; } .input-medium { width: 150px; } .input-large { width: 210px; } .input-xlarge { width: 270px; } .input-xxlarge { width: 530px; } input[class*="span"], select[class*="span"], textarea[class*="span"], .uneditable-input[class*="span"], .row-fluid input[class*="span"], .row-fluid select[class*="span"], .row-fluid textarea[class*="span"], .row-fluid .uneditable-input[class*="span"] { float: none; margin-left: 0; } .input-append input[class*="span"], .input-append .uneditable-input[class*="span"], .input-prepend input[class*="span"], .input-prepend .uneditable-input[class*="span"], .row-fluid input[class*="span"], .row-fluid select[class*="span"], .row-fluid textarea[class*="span"], .row-fluid .uneditable-input[class*="span"], .row-fluid .input-prepend [class*="span"], .row-fluid .input-append [class*="span"] { display: inline-block; } input, textarea, .uneditable-input { margin-left: 0; } .controls-row [class*="span"] + [class*="span"] { margin-left: 20px; } input.span12, textarea.span12, .uneditable-input.span12 { width: 926px; } input.span11, textarea.span11, .uneditable-input.span11 { width: 846px; } input.span10, textarea.span10, .uneditable-input.span10 { width: 766px; } input.span9, textarea.span9, .uneditable-input.span9 { width: 686px; } input.span8, textarea.span8, .uneditable-input.span8 { width: 606px; } input.span7, textarea.span7, .uneditable-input.span7 { width: 526px; } input.span6, textarea.span6, .uneditable-input.span6 { width: 446px; } input.span5, textarea.span5, .uneditable-input.span5 { width: 366px; } input.span4, textarea.span4, .uneditable-input.span4 { width: 286px; } input.span3, textarea.span3, .uneditable-input.span3 { width: 206px; } input.span2, textarea.span2, .uneditable-input.span2 { width: 126px; } input.span1, textarea.span1, .uneditable-input.span1 { width: 46px; } .controls-row { *zoom: 1; } .controls-row:before, .controls-row:after { display: table; line-height: 0; content: ""; } .controls-row:after { clear: both; } .controls-row [class*="span"], .row-fluid .controls-row [class*="span"] { float: left; } .controls-row .checkbox[class*="span"], .controls-row .radio[class*="span"] { padding-top: 5px; } input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] { cursor: not-allowed; background-color: #eeeeee; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"][readonly], input[type="checkbox"][readonly] { background-color: transparent; } .control-group.warning .control-label, .control-group.warning .help-block, .control-group.warning .help-inline { color: #c09853; } .control-group.warning .checkbox, .control-group.warning .radio, .control-group.warning input, .control-group.warning select, .control-group.warning textarea { color: #c09853; } .control-group.warning input, .control-group.warning select, .control-group.warning textarea { border-color: #c09853; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .control-group.warning input:focus, .control-group.warning select:focus, .control-group.warning textarea:focus { border-color: #a47e3c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; } .control-group.warning .input-prepend .add-on, .control-group.warning .input-append .add-on { color: #c09853; background-color: #fcf8e3; border-color: #c09853; } .control-group.error .control-label, .control-group.error .help-block, .control-group.error .help-inline { color: #b94a48; } .control-group.error .checkbox, .control-group.error .radio, .control-group.error input, .control-group.error select, .control-group.error textarea { color: #b94a48; } .control-group.error input, .control-group.error select, .control-group.error textarea { border-color: #b94a48; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .control-group.error input:focus, .control-group.error select:focus, .control-group.error textarea:focus { border-color: #953b39; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; } .control-group.error .input-prepend .add-on, .control-group.error .input-append .add-on { color: #b94a48; background-color: #f2dede; border-color: #b94a48; } .control-group.success .control-label, .control-group.success .help-block, .control-group.success .help-inline { color: #468847; } .control-group.success .checkbox, .control-group.success .radio, .control-group.success input, .control-group.success select, .control-group.success textarea { color: #468847; } .control-group.success input, .control-group.success select, .control-group.success textarea { border-color: #468847; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .control-group.success input:focus, .control-group.success select:focus, .control-group.success textarea:focus { border-color: #356635; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; } .control-group.success .input-prepend .add-on, .control-group.success .input-append .add-on { color: #468847; background-color: #dff0d8; border-color: #468847; } .control-group.info .control-label, .control-group.info .help-block, .control-group.info .help-inline { color: #3a87ad; } .control-group.info .checkbox, .control-group.info .radio, .control-group.info input, .control-group.info select, .control-group.info textarea { color: #3a87ad; } .control-group.info input, .control-group.info select, .control-group.info textarea { border-color: #3a87ad; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .control-group.info input:focus, .control-group.info select:focus, .control-group.info textarea:focus { border-color: #2d6987; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; } .control-group.info .input-prepend .add-on, .control-group.info .input-append .add-on { color: #3a87ad; background-color: #d9edf7; border-color: #3a87ad; } input:focus:invalid, textarea:focus:invalid, select:focus:invalid { color: #b94a48; border-color: #ee5f5b; } input:focus:invalid:focus, textarea:focus:invalid:focus, select:focus:invalid:focus { border-color: #e9322d; -webkit-box-shadow: 0 0 6px #f8b9b7; -moz-box-shadow: 0 0 6px #f8b9b7; box-shadow: 0 0 6px #f8b9b7; } .form-actions { padding: 19px 20px 20px; margin-top: 20px; margin-bottom: 20px; background-color: #f5f5f5; border-top: 1px solid #e5e5e5; *zoom: 1; } .form-actions:before, .form-actions:after { display: table; line-height: 0; content: ""; } .form-actions:after { clear: both; } .help-block, .help-inline { color: #595959; } .help-block { display: block; margin-bottom: 10px; } .help-inline { display: inline-block; *display: inline; padding-left: 5px; vertical-align: middle; *zoom: 1; } .input-append, .input-prepend { display: inline-block; margin-bottom: 10px; font-size: 0; white-space: nowrap; vertical-align: middle; } .input-append input, .input-prepend input, .input-append select, .input-prepend select, .input-append .uneditable-input, .input-prepend .uneditable-input, .input-append .dropdown-menu, .input-prepend .dropdown-menu, .input-append .popover, .input-prepend .popover { font-size: 14px; } .input-append input, .input-prepend input, .input-append select, .input-prepend select, .input-append .uneditable-input, .input-prepend .uneditable-input { position: relative; margin-bottom: 0; *margin-left: 0; vertical-align: top; -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .input-append input:focus, .input-prepend input:focus, .input-append select:focus, .input-prepend select:focus, .input-append .uneditable-input:focus, .input-prepend .uneditable-input:focus { z-index: 2; } .input-append .add-on, .input-prepend .add-on { display: inline-block; width: auto; height: 20px; min-width: 16px; padding: 4px 5px; font-size: 14px; font-weight: normal; line-height: 20px; text-align: center; text-shadow: 0 1px 0 #ffffff; background-color: #eeeeee; border: 1px solid #ccc; } .input-append .add-on, .input-prepend .add-on, .input-append .btn, .input-prepend .btn, .input-append .btn-group > .dropdown-toggle, .input-prepend .btn-group > .dropdown-toggle { vertical-align: top; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .input-append .active, .input-prepend .active { background-color: #a9dba9; border-color: #46a546; } .input-prepend .add-on, .input-prepend .btn { margin-right: -1px; } .input-prepend .add-on:first-child, .input-prepend .btn:first-child { -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } .input-append input, .input-append select, .input-append .uneditable-input { -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } .input-append input + .btn-group .btn:last-child, .input-append select + .btn-group .btn:last-child, .input-append .uneditable-input + .btn-group .btn:last-child { -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .input-append .add-on, .input-append .btn, .input-append .btn-group { margin-left: -1px; } .input-append .add-on:last-child, .input-append .btn:last-child, .input-append .btn-group:last-child > .dropdown-toggle { -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .input-prepend.input-append input, .input-prepend.input-append select, .input-prepend.input-append .uneditable-input { -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .input-prepend.input-append input + .btn-group .btn, .input-prepend.input-append select + .btn-group .btn, .input-prepend.input-append .uneditable-input + .btn-group .btn { -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .input-prepend.input-append .add-on:first-child, .input-prepend.input-append .btn:first-child { margin-right: -1px; -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } .input-prepend.input-append .add-on:last-child, .input-prepend.input-append .btn:last-child { margin-left: -1px; -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .input-prepend.input-append .btn-group:first-child { margin-left: 0; } input.search-query { padding-right: 14px; padding-right: 4px \9; padding-left: 14px; padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ margin-bottom: 0; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px; } /* Allow for input prepend/append in search forms */ .form-search .input-append .search-query, .form-search .input-prepend .search-query { -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .form-search .input-append .search-query { -webkit-border-radius: 14px 0 0 14px; -moz-border-radius: 14px 0 0 14px; border-radius: 14px 0 0 14px; } .form-search .input-append .btn { -webkit-border-radius: 0 14px 14px 0; -moz-border-radius: 0 14px 14px 0; border-radius: 0 14px 14px 0; } .form-search .input-prepend .search-query { -webkit-border-radius: 0 14px 14px 0; -moz-border-radius: 0 14px 14px 0; border-radius: 0 14px 14px 0; } .form-search .input-prepend .btn { -webkit-border-radius: 14px 0 0 14px; -moz-border-radius: 14px 0 0 14px; border-radius: 14px 0 0 14px; } .form-search input, .form-inline input, .form-horizontal input, .form-search textarea, .form-inline textarea, .form-horizontal textarea, .form-search select, .form-inline select, .form-horizontal select, .form-search .help-inline, .form-inline .help-inline, .form-horizontal .help-inline, .form-search .uneditable-input, .form-inline .uneditable-input, .form-horizontal .uneditable-input, .form-search .input-prepend, .form-inline .input-prepend, .form-horizontal .input-prepend, .form-search .input-append, .form-inline .input-append, .form-horizontal .input-append { display: inline-block; *display: inline; margin-bottom: 0; vertical-align: middle; *zoom: 1; } .form-search .hide, .form-inline .hide, .form-horizontal .hide { display: none; } .form-search label, .form-inline label, .form-search .btn-group, .form-inline .btn-group { display: inline-block; } .form-search .input-append, .form-inline .input-append, .form-search .input-prepend, .form-inline .input-prepend { margin-bottom: 0; } .form-search .radio, .form-search .checkbox, .form-inline .radio, .form-inline .checkbox { padding-left: 0; margin-bottom: 0; vertical-align: middle; } .form-search .radio input[type="radio"], .form-search .checkbox input[type="checkbox"], .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { float: left; margin-right: 3px; margin-left: 0; } .control-group { margin-bottom: 10px; } legend + .control-group { margin-top: 20px; -webkit-margin-top-collapse: separate; } .form-horizontal .control-group { margin-bottom: 20px; *zoom: 1; } .form-horizontal .control-group:before, .form-horizontal .control-group:after { display: table; line-height: 0; content: ""; } .form-horizontal .control-group:after { clear: both; } .form-horizontal .control-label { float: left; width: 160px; padding-top: 5px; text-align: right; } .form-horizontal .controls { *display: inline-block; *padding-left: 20px; margin-left: 180px; *margin-left: 0; } .form-horizontal .controls:first-child { *padding-left: 180px; } .form-horizontal .help-block { margin-bottom: 0; } .form-horizontal input + .help-block, .form-horizontal select + .help-block, .form-horizontal textarea + .help-block, .form-horizontal .uneditable-input + .help-block, .form-horizontal .input-prepend + .help-block, .form-horizontal .input-append + .help-block { margin-top: 10px; } .form-horizontal .form-actions { padding-left: 180px; } table { max-width: 100%; background-color: transparent; border-collapse: collapse; border-spacing: 0; } .table { width: 100%; margin-bottom: 20px; } .table th, .table td { padding: 8px; line-height: 20px; text-align: left; vertical-align: top; border-top: 1px solid #dddddd; } .table th { font-weight: bold; } .table thead th { vertical-align: bottom; } .table caption + thead tr:first-child th, .table caption + thead tr:first-child td, .table colgroup + thead tr:first-child th, .table colgroup + thead tr:first-child td, .table thead:first-child tr:first-child th, .table thead:first-child tr:first-child td { border-top: 0; } .table tbody + tbody { border-top: 2px solid #dddddd; } .table .table { background-color: #ffffff; } .table-condensed th, .table-condensed td { padding: 4px 5px; } .table-bordered { border: 1px solid #dddddd; border-collapse: separate; *border-collapse: collapse; border-left: 0; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .table-bordered th, .table-bordered td { border-left: 1px solid #dddddd; } .table-bordered caption + thead tr:first-child th, .table-bordered caption + tbody tr:first-child th, .table-bordered caption + tbody tr:first-child td, .table-bordered colgroup + thead tr:first-child th, .table-bordered colgroup + tbody tr:first-child th, .table-bordered colgroup + tbody tr:first-child td, .table-bordered thead:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child td { border-top: 0; } .table-bordered thead:first-child tr:first-child > th:first-child, .table-bordered tbody:first-child tr:first-child > td:first-child, .table-bordered tbody:first-child tr:first-child > th:first-child { -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topleft: 4px; } .table-bordered thead:first-child tr:first-child > th:last-child, .table-bordered tbody:first-child tr:first-child > td:last-child, .table-bordered tbody:first-child tr:first-child > th:last-child { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-topright: 4px; } .table-bordered thead:last-child tr:last-child > th:first-child, .table-bordered tbody:last-child tr:last-child > td:first-child, .table-bordered tbody:last-child tr:last-child > th:first-child, .table-bordered tfoot:last-child tr:last-child > td:first-child, .table-bordered tfoot:last-child tr:last-child > th:first-child { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomleft: 4px; } .table-bordered thead:last-child tr:last-child > th:last-child, .table-bordered tbody:last-child tr:last-child > td:last-child, .table-bordered tbody:last-child tr:last-child > th:last-child, .table-bordered tfoot:last-child tr:last-child > td:last-child, .table-bordered tfoot:last-child tr:last-child > th:last-child { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-bottomright: 4px; } .table-bordered tfoot + tbody:last-child tr:last-child td:first-child { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -moz-border-radius-bottomleft: 0; } .table-bordered tfoot + tbody:last-child tr:last-child td:last-child { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -moz-border-radius-bottomright: 0; } .table-bordered caption + thead tr:first-child th:first-child, .table-bordered caption + tbody tr:first-child td:first-child, .table-bordered colgroup + thead tr:first-child th:first-child, .table-bordered colgroup + tbody tr:first-child td:first-child { -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topleft: 4px; } .table-bordered caption + thead tr:first-child th:last-child, .table-bordered caption + tbody tr:first-child td:last-child, .table-bordered colgroup + thead tr:first-child th:last-child, .table-bordered colgroup + tbody tr:first-child td:last-child { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-topright: 4px; } .table-striped tbody > tr:nth-child(odd) > td, .table-striped tbody > tr:nth-child(odd) > th { background-color: #f9f9f9; } .table-hover tbody tr:hover > td, .table-hover tbody tr:hover > th { background-color: #f5f5f5; } table td[class*="span"], table th[class*="span"], .row-fluid table td[class*="span"], .row-fluid table th[class*="span"] { display: table-cell; float: none; margin-left: 0; } .table td.span1, .table th.span1 { float: none; width: 44px; margin-left: 0; } .table td.span2, .table th.span2 { float: none; width: 124px; margin-left: 0; } .table td.span3, .table th.span3 { float: none; width: 204px; margin-left: 0; } .table td.span4, .table th.span4 { float: none; width: 284px; margin-left: 0; } .table td.span5, .table th.span5 { float: none; width: 364px; margin-left: 0; } .table td.span6, .table th.span6 { float: none; width: 444px; margin-left: 0; } .table td.span7, .table th.span7 { float: none; width: 524px; margin-left: 0; } .table td.span8, .table th.span8 { float: none; width: 604px; margin-left: 0; } .table td.span9, .table th.span9 { float: none; width: 684px; margin-left: 0; } .table td.span10, .table th.span10 { float: none; width: 764px; margin-left: 0; } .table td.span11, .table th.span11 { float: none; width: 844px; margin-left: 0; } .table td.span12, .table th.span12 { float: none; width: 924px; margin-left: 0; } .table tbody tr.success > td { background-color: #dff0d8; } .table tbody tr.error > td { background-color: #f2dede; } .table tbody tr.warning > td { background-color: #fcf8e3; } .table tbody tr.info > td { background-color: #d9edf7; } .table-hover tbody tr.success:hover > td { background-color: #d0e9c6; } .table-hover tbody tr.error:hover > td { background-color: #ebcccc; } .table-hover tbody tr.warning:hover > td { background-color: #faf2cc; } .table-hover tbody tr.info:hover > td { background-color: #c4e3f3; } [class^="icon-"], [class*=" icon-"] { display: inline-block; width: 14px; height: 14px; margin-top: 1px; *margin-right: .3em; line-height: 14px; vertical-align: text-top; background-image: url("../img/glyphicons-halflings.png"); background-position: 14px 14px; background-repeat: no-repeat; } /* White icons with optional class, or on hover/focus/active states of certain elements */ .icon-white, .nav-pills > .active > a > [class^="icon-"], .nav-pills > .active > a > [class*=" icon-"], .nav-list > .active > a > [class^="icon-"], .nav-list > .active > a > [class*=" icon-"], .navbar-inverse .nav > .active > a > [class^="icon-"], .navbar-inverse .nav > .active > a > [class*=" icon-"], .dropdown-menu > li > a:hover > [class^="icon-"], .dropdown-menu > li > a:focus > [class^="icon-"], .dropdown-menu > li > a:hover > [class*=" icon-"], .dropdown-menu > li > a:focus > [class*=" icon-"], .dropdown-menu > .active > a > [class^="icon-"], .dropdown-menu > .active > a > [class*=" icon-"], .dropdown-submenu:hover > a > [class^="icon-"], .dropdown-submenu:focus > a > [class^="icon-"], .dropdown-submenu:hover > a > [class*=" icon-"], .dropdown-submenu:focus > a > [class*=" icon-"] { background-image: url("../img/glyphicons-halflings-white.png"); } .icon-glass { background-position: 0 0; } .icon-music { background-position: -24px 0; } .icon-search { background-position: -48px 0; } .icon-envelope { background-position: -72px 0; } .icon-heart { background-position: -96px 0; } .icon-star { background-position: -120px 0; } .icon-star-empty { background-position: -144px 0; } .icon-user { background-position: -168px 0; } .icon-film { background-position: -192px 0; } .icon-th-large { background-position: -216px 0; } .icon-th { background-position: -240px 0; } .icon-th-list { background-position: -264px 0; } .icon-ok { background-position: -288px 0; } .icon-remove { background-position: -312px 0; } .icon-zoom-in { background-position: -336px 0; } .icon-zoom-out { background-position: -360px 0; } .icon-off { background-position: -384px 0; } .icon-signal { background-position: -408px 0; } .icon-cog { background-position: -432px 0; } .icon-trash { background-position: -456px 0; } .icon-home { background-position: 0 -24px; } .icon-file { background-position: -24px -24px; } .icon-time { background-position: -48px -24px; } .icon-road { background-position: -72px -24px; } .icon-download-alt { background-position: -96px -24px; } .icon-download { background-position: -120px -24px; } .icon-upload { background-position: -144px -24px; } .icon-inbox { background-position: -168px -24px; } .icon-play-circle { background-position: -192px -24px; } .icon-repeat { background-position: -216px -24px; } .icon-refresh { background-position: -240px -24px; } .icon-list-alt { background-position: -264px -24px; } .icon-lock { background-position: -287px -24px; } .icon-flag { background-position: -312px -24px; } .icon-headphones { background-position: -336px -24px; } .icon-volume-off { background-position: -360px -24px; } .icon-volume-down { background-position: -384px -24px; } .icon-volume-up { background-position: -408px -24px; } .icon-qrcode { background-position: -432px -24px; } .icon-barcode { background-position: -456px -24px; } .icon-tag { background-position: 0 -48px; } .icon-tags { background-position: -25px -48px; } .icon-book { background-position: -48px -48px; } .icon-bookmark { background-position: -72px -48px; } .icon-print { background-position: -96px -48px; } .icon-camera { background-position: -120px -48px; } .icon-font { background-position: -144px -48px; } .icon-bold { background-position: -167px -48px; } .icon-italic { background-position: -192px -48px; } .icon-text-height { background-position: -216px -48px; } .icon-text-width { background-position: -240px -48px; } .icon-align-left { background-position: -264px -48px; } .icon-align-center { background-position: -288px -48px; } .icon-align-right { background-position: -312px -48px; } .icon-align-justify { background-position: -336px -48px; } .icon-list { background-position: -360px -48px; } .icon-indent-left { background-position: -384px -48px; } .icon-indent-right { background-position: -408px -48px; } .icon-facetime-video { background-position: -432px -48px; } .icon-picture { background-position: -456px -48px; } .icon-pencil { background-position: 0 -72px; } .icon-map-marker { background-position: -24px -72px; } .icon-adjust { background-position: -48px -72px; } .icon-tint { background-position: -72px -72px; } .icon-edit { background-position: -96px -72px; } .icon-share { background-position: -120px -72px; } .icon-check { background-position: -144px -72px; } .icon-move { background-position: -168px -72px; } .icon-step-backward { background-position: -192px -72px; } .icon-fast-backward { background-position: -216px -72px; } .icon-backward { background-position: -240px -72px; } .icon-play { background-position: -264px -72px; } .icon-pause { background-position: -288px -72px; } .icon-stop { background-position: -312px -72px; } .icon-forward { background-position: -336px -72px; } .icon-fast-forward { background-position: -360px -72px; } .icon-step-forward { background-position: -384px -72px; } .icon-eject { background-position: -408px -72px; } .icon-chevron-left { background-position: -432px -72px; } .icon-chevron-right { background-position: -456px -72px; } .icon-plus-sign { background-position: 0 -96px; } .icon-minus-sign { background-position: -24px -96px; } .icon-remove-sign { background-position: -48px -96px; } .icon-ok-sign { background-position: -72px -96px; } .icon-question-sign { background-position: -96px -96px; } .icon-info-sign { background-position: -120px -96px; } .icon-screenshot { background-position: -144px -96px; } .icon-remove-circle { background-position: -168px -96px; } .icon-ok-circle { background-position: -192px -96px; } .icon-ban-circle { background-position: -216px -96px; } .icon-arrow-left { background-position: -240px -96px; } .icon-arrow-right { background-position: -264px -96px; } .icon-arrow-up { background-position: -289px -96px; } .icon-arrow-down { background-position: -312px -96px; } .icon-share-alt { background-position: -336px -96px; } .icon-resize-full { background-position: -360px -96px; } .icon-resize-small { background-position: -384px -96px; } .icon-plus { background-position: -408px -96px; } .icon-minus { background-position: -433px -96px; } .icon-asterisk { background-position: -456px -96px; } .icon-exclamation-sign { background-position: 0 -120px; } .icon-gift { background-position: -24px -120px; } .icon-leaf { background-position: -48px -120px; } .icon-fire { background-position: -72px -120px; } .icon-eye-open { background-position: -96px -120px; } .icon-eye-close { background-position: -120px -120px; } .icon-warning-sign { background-position: -144px -120px; } .icon-plane { background-position: -168px -120px; } .icon-calendar { background-position: -192px -120px; } .icon-random { width: 16px; background-position: -216px -120px; } .icon-comment { background-position: -240px -120px; } .icon-magnet { background-position: -264px -120px; } .icon-chevron-up { background-position: -288px -120px; } .icon-chevron-down { background-position: -313px -119px; } .icon-retweet { background-position: -336px -120px; } .icon-shopping-cart { background-position: -360px -120px; } .icon-folder-close { width: 16px; background-position: -384px -120px; } .icon-folder-open { width: 16px; background-position: -408px -120px; } .icon-resize-vertical { background-position: -432px -119px; } .icon-resize-horizontal { background-position: -456px -118px; } .icon-hdd { background-position: 0 -144px; } .icon-bullhorn { background-position: -24px -144px; } .icon-bell { background-position: -48px -144px; } .icon-certificate { background-position: -72px -144px; } .icon-thumbs-up { background-position: -96px -144px; } .icon-thumbs-down { background-position: -120px -144px; } .icon-hand-right { background-position: -144px -144px; } .icon-hand-left { background-position: -168px -144px; } .icon-hand-up { background-position: -192px -144px; } .icon-hand-down { background-position: -216px -144px; } .icon-circle-arrow-right { background-position: -240px -144px; } .icon-circle-arrow-left { background-position: -264px -144px; } .icon-circle-arrow-up { background-position: -288px -144px; } .icon-circle-arrow-down { background-position: -312px -144px; } .icon-globe { background-position: -336px -144px; } .icon-wrench { background-position: -360px -144px; } .icon-tasks { background-position: -384px -144px; } .icon-filter { background-position: -408px -144px; } .icon-briefcase { background-position: -432px -144px; } .icon-fullscreen { background-position: -456px -144px; } .dropup, .dropdown { position: relative; } .dropdown-toggle { *margin-bottom: -3px; } .dropdown-toggle:active, .open .dropdown-toggle { outline: 0; } .caret { display: inline-block; width: 0; height: 0; vertical-align: top; border-top: 4px solid #000000; border-right: 4px solid transparent; border-left: 4px solid transparent; content: ""; } .dropdown .caret { margin-top: 8px; margin-left: 2px; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; list-style: none; background-color: #ffffff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); *border-right-width: 2px; *border-bottom-width: 2px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { *width: 100%; height: 1px; margin: 9px 1px; *margin: -5px 0 5px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid #ffffff; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 20px; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus, .dropdown-submenu:hover > a, .dropdown-submenu:focus > a { color: #ffffff; text-decoration: none; background-color: #0081c2; background-image: -moz-linear-gradient(top, #0088cc, #0077b3); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); background-image: -o-linear-gradient(top, #0088cc, #0077b3); background-image: linear-gradient(to bottom, #0088cc, #0077b3); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #ffffff; text-decoration: none; background-color: #0081c2; background-image: -moz-linear-gradient(top, #0088cc, #0077b3); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); background-image: -o-linear-gradient(top, #0088cc, #0077b3); background-image: linear-gradient(to bottom, #0088cc, #0077b3); background-repeat: repeat-x; outline: 0; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #999999; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; cursor: default; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .open { *z-index: 1000; } .open > .dropdown-menu { display: block; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0; border-bottom: 4px solid #000000; content: ""; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } .dropdown-submenu { position: relative; } .dropdown-submenu > .dropdown-menu { top: 0; left: 100%; margin-top: -6px; margin-left: -1px; -webkit-border-radius: 0 6px 6px 6px; -moz-border-radius: 0 6px 6px 6px; border-radius: 0 6px 6px 6px; } .dropdown-submenu:hover > .dropdown-menu { display: block; } .dropup .dropdown-submenu > .dropdown-menu { top: auto; bottom: 0; margin-top: 0; margin-bottom: -2px; -webkit-border-radius: 5px 5px 5px 0; -moz-border-radius: 5px 5px 5px 0; border-radius: 5px 5px 5px 0; } .dropdown-submenu > a:after { display: block; float: right; width: 0; height: 0; margin-top: 5px; margin-right: -10px; border-color: transparent; border-left-color: #cccccc; border-style: solid; border-width: 5px 0 5px 5px; content: " "; } .dropdown-submenu:hover > a:after { border-left-color: #ffffff; } .dropdown-submenu.pull-left { float: none; } .dropdown-submenu.pull-left > .dropdown-menu { left: -100%; margin-left: 10px; -webkit-border-radius: 6px 0 6px 6px; -moz-border-radius: 6px 0 6px 6px; border-radius: 6px 0 6px 6px; } .dropdown .dropdown-menu .nav-header { padding-right: 20px; padding-left: 20px; } .typeahead { z-index: 1051; margin-top: 2px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-large { padding: 24px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .well-small { padding: 9px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; -moz-transition: opacity 0.15s linear; -o-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { position: relative; height: 0; overflow: hidden; -webkit-transition: height 0.35s ease; -moz-transition: height 0.35s ease; -o-transition: height 0.35s ease; transition: height 0.35s ease; } .collapse.in { height: auto; } .close { float: right; font-size: 20px; font-weight: bold; line-height: 20px; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover, .close:focus { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.4; filter: alpha(opacity=40); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .btn { display: inline-block; *display: inline; padding: 4px 12px; margin-bottom: 0; *margin-left: .3em; font-size: 14px; line-height: 20px; color: #333333; text-align: center; text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; cursor: pointer; background-color: #f5f5f5; *background-color: #e6e6e6; background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); background-repeat: repeat-x; border: 1px solid #cccccc; *border: 0; border-color: #e6e6e6 #e6e6e6 #bfbfbf; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border-bottom-color: #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); *zoom: 1; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn:hover, .btn:focus, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { color: #333333; background-color: #e6e6e6; *background-color: #d9d9d9; } .btn:active, .btn.active { background-color: #cccccc \9; } .btn:first-child { *margin-left: 0; } .btn:hover, .btn:focus { color: #333333; text-decoration: none; background-position: 0 -15px; -webkit-transition: background-position 0.1s linear; -moz-transition: background-position 0.1s linear; -o-transition: background-position 0.1s linear; transition: background-position 0.1s linear; } .btn:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn.active, .btn:active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn.disabled, .btn[disabled] { cursor: default; background-image: none; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } .btn-large { padding: 11px 19px; font-size: 17.5px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .btn-large [class^="icon-"], .btn-large [class*=" icon-"] { margin-top: 4px; } .btn-small { padding: 2px 10px; font-size: 11.9px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .btn-small [class^="icon-"], .btn-small [class*=" icon-"] { margin-top: 0; } .btn-mini [class^="icon-"], .btn-mini [class*=" icon-"] { margin-top: -1px; } .btn-mini { padding: 0 6px; font-size: 10.5px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .btn-block { display: block; width: 100%; padding-right: 0; padding-left: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .btn-primary.active, .btn-warning.active, .btn-danger.active, .btn-success.active, .btn-info.active, .btn-inverse.active { color: rgba(255, 255, 255, 0.75); } .btn-primary { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #006dcc; *background-color: #0044cc; background-image: -moz-linear-gradient(top, #0088cc, #0044cc); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); background-image: -o-linear-gradient(top, #0088cc, #0044cc); background-image: linear-gradient(to bottom, #0088cc, #0044cc); background-repeat: repeat-x; border-color: #0044cc #0044cc #002a80; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { color: #ffffff; background-color: #0044cc; *background-color: #003bb3; } .btn-primary:active, .btn-primary.active { background-color: #003399 \9; } .btn-warning { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #faa732; *background-color: #f89406; background-image: -moz-linear-gradient(top, #fbb450, #f89406); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); background-image: -webkit-linear-gradient(top, #fbb450, #f89406); background-image: -o-linear-gradient(top, #fbb450, #f89406); background-image: linear-gradient(to bottom, #fbb450, #f89406); background-repeat: repeat-x; border-color: #f89406 #f89406 #ad6704; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active, .btn-warning.disabled, .btn-warning[disabled] { color: #ffffff; background-color: #f89406; *background-color: #df8505; } .btn-warning:active, .btn-warning.active { background-color: #c67605 \9; } .btn-danger { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #da4f49; *background-color: #bd362f; background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); background-repeat: repeat-x; border-color: #bd362f #bd362f #802420; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active, .btn-danger.disabled, .btn-danger[disabled] { color: #ffffff; background-color: #bd362f; *background-color: #a9302a; } .btn-danger:active, .btn-danger.active { background-color: #942a25 \9; } .btn-success { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #5bb75b; *background-color: #51a351; background-image: -moz-linear-gradient(top, #62c462, #51a351); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); background-image: -webkit-linear-gradient(top, #62c462, #51a351); background-image: -o-linear-gradient(top, #62c462, #51a351); background-image: linear-gradient(to bottom, #62c462, #51a351); background-repeat: repeat-x; border-color: #51a351 #51a351 #387038; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active, .btn-success.disabled, .btn-success[disabled] { color: #ffffff; background-color: #51a351; *background-color: #499249; } .btn-success:active, .btn-success.active { background-color: #408140 \9; } .btn-info { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #49afcd; *background-color: #2f96b4; background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); background-repeat: repeat-x; border-color: #2f96b4 #2f96b4 #1f6377; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .btn-info:hover, .btn-info:focus, .btn-info:active, .btn-info.active, .btn-info.disabled, .btn-info[disabled] { color: #ffffff; background-color: #2f96b4; *background-color: #2a85a0; } .btn-info:active, .btn-info.active { background-color: #24748c \9; } .btn-inverse { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #363636; *background-color: #222222; background-image: -moz-linear-gradient(top, #444444, #222222); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); background-image: -webkit-linear-gradient(top, #444444, #222222); background-image: -o-linear-gradient(top, #444444, #222222); background-image: linear-gradient(to bottom, #444444, #222222); background-repeat: repeat-x; border-color: #222222 #222222 #000000; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .btn-inverse:hover, .btn-inverse:focus, .btn-inverse:active, .btn-inverse.active, .btn-inverse.disabled, .btn-inverse[disabled] { color: #ffffff; background-color: #222222; *background-color: #151515; } .btn-inverse:active, .btn-inverse.active { background-color: #080808 \9; } button.btn, input[type="submit"].btn { *padding-top: 3px; *padding-bottom: 3px; } button.btn::-moz-focus-inner, input[type="submit"].btn::-moz-focus-inner { padding: 0; border: 0; } button.btn.btn-large, input[type="submit"].btn.btn-large { *padding-top: 7px; *padding-bottom: 7px; } button.btn.btn-small, input[type="submit"].btn.btn-small { *padding-top: 3px; *padding-bottom: 3px; } button.btn.btn-mini, input[type="submit"].btn.btn-mini { *padding-top: 1px; *padding-bottom: 1px; } .btn-link, .btn-link:active, .btn-link[disabled] { background-color: transparent; background-image: none; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } .btn-link { color: #0088cc; cursor: pointer; border-color: transparent; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .btn-link:hover, .btn-link:focus { color: #005580; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, .btn-link[disabled]:focus { color: #333333; text-decoration: none; } .btn-group { position: relative; display: inline-block; *display: inline; *margin-left: .3em; font-size: 0; white-space: nowrap; vertical-align: middle; *zoom: 1; } .btn-group:first-child { *margin-left: 0; } .btn-group + .btn-group { margin-left: 5px; } .btn-toolbar { margin-top: 10px; margin-bottom: 10px; font-size: 0; } .btn-toolbar > .btn + .btn, .btn-toolbar > .btn-group + .btn, .btn-toolbar > .btn + .btn-group { margin-left: 5px; } .btn-group > .btn { position: relative; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .btn-group > .btn + .btn { margin-left: -1px; } .btn-group > .btn, .btn-group > .dropdown-menu, .btn-group > .popover { font-size: 14px; } .btn-group > .btn-mini { font-size: 10.5px; } .btn-group > .btn-small { font-size: 11.9px; } .btn-group > .btn-large { font-size: 17.5px; } .btn-group > .btn:first-child { margin-left: 0; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -moz-border-radius-topleft: 4px; } .btn-group > .btn:last-child, .btn-group > .dropdown-toggle { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-topright: 4px; -moz-border-radius-bottomright: 4px; } .btn-group > .btn.large:first-child { margin-left: 0; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-bottomleft: 6px; -moz-border-radius-topleft: 6px; } .btn-group > .btn.large:last-child, .btn-group > .large.dropdown-toggle { -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; -moz-border-radius-topright: 6px; -moz-border-radius-bottomright: 6px; } .btn-group > .btn:hover, .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active { z-index: 2; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group > .btn + .dropdown-toggle { *padding-top: 5px; padding-right: 8px; *padding-bottom: 5px; padding-left: 8px; -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn-group > .btn-mini + .dropdown-toggle { *padding-top: 2px; padding-right: 5px; *padding-bottom: 2px; padding-left: 5px; } .btn-group > .btn-small + .dropdown-toggle { *padding-top: 5px; *padding-bottom: 4px; } .btn-group > .btn-large + .dropdown-toggle { *padding-top: 7px; padding-right: 12px; *padding-bottom: 7px; padding-left: 12px; } .btn-group.open .dropdown-toggle { background-image: none; -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn-group.open .btn.dropdown-toggle { background-color: #e6e6e6; } .btn-group.open .btn-primary.dropdown-toggle { background-color: #0044cc; } .btn-group.open .btn-warning.dropdown-toggle { background-color: #f89406; } .btn-group.open .btn-danger.dropdown-toggle { background-color: #bd362f; } .btn-group.open .btn-success.dropdown-toggle { background-color: #51a351; } .btn-group.open .btn-info.dropdown-toggle { background-color: #2f96b4; } .btn-group.open .btn-inverse.dropdown-toggle { background-color: #222222; } .btn .caret { margin-top: 8px; margin-left: 0; } .btn-large .caret { margin-top: 6px; } .btn-large .caret { border-top-width: 5px; border-right-width: 5px; border-left-width: 5px; } .btn-mini .caret, .btn-small .caret { margin-top: 8px; } .dropup .btn-large .caret { border-bottom-width: 5px; } .btn-primary .caret, .btn-warning .caret, .btn-danger .caret, .btn-info .caret, .btn-success .caret, .btn-inverse .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } .btn-group-vertical { display: inline-block; *display: inline; /* IE7 inline-block hack */ *zoom: 1; } .btn-group-vertical > .btn { display: block; float: none; max-width: 100%; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .btn-group-vertical > .btn + .btn { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:first-child { -webkit-border-radius: 4px 4px 0 0; -moz-border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0; } .btn-group-vertical > .btn:last-child { -webkit-border-radius: 0 0 4px 4px; -moz-border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px; } .btn-group-vertical > .btn-large:first-child { -webkit-border-radius: 6px 6px 0 0; -moz-border-radius: 6px 6px 0 0; border-radius: 6px 6px 0 0; } .btn-group-vertical > .btn-large:last-child { -webkit-border-radius: 0 0 6px 6px; -moz-border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px; } .alert { padding: 8px 35px 8px 14px; margin-bottom: 20px; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); background-color: #fcf8e3; border: 1px solid #fbeed5; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .alert, .alert h4 { color: #c09853; } .alert h4 { margin: 0; } .alert .close { position: relative; top: -2px; right: -21px; line-height: 20px; } .alert-success { color: #468847; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success h4 { color: #468847; } .alert-danger, .alert-error { color: #b94a48; background-color: #f2dede; border-color: #eed3d7; } .alert-danger h4, .alert-error h4 { color: #b94a48; } .alert-info { color: #3a87ad; background-color: #d9edf7; border-color: #bce8f1; } .alert-info h4 { color: #3a87ad; } .alert-block { padding-top: 14px; padding-bottom: 14px; } .alert-block > p, .alert-block > ul { margin-bottom: 0; } .alert-block p + p { margin-top: 5px; } .nav { margin-bottom: 20px; margin-left: 0; list-style: none; } .nav > li > a { display: block; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eeeeee; } .nav > li > a > img { max-width: none; } .nav > .pull-right { float: right; } .nav-header { display: block; padding: 3px 15px; font-size: 11px; font-weight: bold; line-height: 20px; color: #999999; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); text-transform: uppercase; } .nav li + .nav-header { margin-top: 9px; } .nav-list { padding-right: 15px; padding-left: 15px; margin-bottom: 0; } .nav-list > li > a, .nav-list .nav-header { margin-right: -15px; margin-left: -15px; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); } .nav-list > li > a { padding: 3px 15px; } .nav-list > .active > a, .nav-list > .active > a:hover, .nav-list > .active > a:focus { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); background-color: #0088cc; } .nav-list [class^="icon-"], .nav-list [class*=" icon-"] { margin-right: 2px; } .nav-list .divider { *width: 100%; height: 1px; margin: 9px 1px; *margin: -5px 0 5px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid #ffffff; } .nav-tabs, .nav-pills { *zoom: 1; } .nav-tabs:before, .nav-pills:before, .nav-tabs:after, .nav-pills:after { display: table; line-height: 0; content: ""; } .nav-tabs:after, .nav-pills:after { clear: both; } .nav-tabs > li, .nav-pills > li { float: left; } .nav-tabs > li > a, .nav-pills > li > a { padding-right: 12px; padding-left: 12px; margin-right: 2px; line-height: 14px; } .nav-tabs { border-bottom: 1px solid #ddd; } .nav-tabs > li { margin-bottom: -1px; } .nav-tabs > li > a { padding-top: 8px; padding-bottom: 8px; line-height: 20px; border: 1px solid transparent; -webkit-border-radius: 4px 4px 0 0; -moz-border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover, .nav-tabs > li > a:focus { border-color: #eeeeee #eeeeee #dddddd; } .nav-tabs > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus { color: #555555; cursor: default; background-color: #ffffff; border: 1px solid #ddd; border-bottom-color: transparent; } .nav-pills > li > a { padding-top: 8px; padding-bottom: 8px; margin-top: 2px; margin-bottom: 2px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } .nav-pills > .active > a, .nav-pills > .active > a:hover, .nav-pills > .active > a:focus { color: #ffffff; background-color: #0088cc; } .nav-stacked > li { float: none; } .nav-stacked > li > a { margin-right: 0; } .nav-tabs.nav-stacked { border-bottom: 0; } .nav-tabs.nav-stacked > li > a { border: 1px solid #ddd; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .nav-tabs.nav-stacked > li:first-child > a { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -moz-border-radius-topleft: 4px; } .nav-tabs.nav-stacked > li:last-child > a { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -moz-border-radius-bottomleft: 4px; } .nav-tabs.nav-stacked > li > a:hover, .nav-tabs.nav-stacked > li > a:focus { z-index: 2; border-color: #ddd; } .nav-pills.nav-stacked > li > a { margin-bottom: 3px; } .nav-pills.nav-stacked > li:last-child > a { margin-bottom: 1px; } .nav-tabs .dropdown-menu { -webkit-border-radius: 0 0 6px 6px; -moz-border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px; } .nav-pills .dropdown-menu { -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .nav .dropdown-toggle .caret { margin-top: 6px; border-top-color: #0088cc; border-bottom-color: #0088cc; } .nav .dropdown-toggle:hover .caret, .nav .dropdown-toggle:focus .caret { border-top-color: #005580; border-bottom-color: #005580; } /* move down carets for tabs */ .nav-tabs .dropdown-toggle .caret { margin-top: 8px; } .nav .active .dropdown-toggle .caret { border-top-color: #fff; border-bottom-color: #fff; } .nav-tabs .active .dropdown-toggle .caret { border-top-color: #555555; border-bottom-color: #555555; } .nav > .dropdown.active > a:hover, .nav > .dropdown.active > a:focus { cursor: pointer; } .nav-tabs .open .dropdown-toggle, .nav-pills .open .dropdown-toggle, .nav > li.dropdown.open.active > a:hover, .nav > li.dropdown.open.active > a:focus { color: #ffffff; background-color: #999999; border-color: #999999; } .nav li.dropdown.open .caret, .nav li.dropdown.open.active .caret, .nav li.dropdown.open a:hover .caret, .nav li.dropdown.open a:focus .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; opacity: 1; filter: alpha(opacity=100); } .tabs-stacked .open > a:hover, .tabs-stacked .open > a:focus { border-color: #999999; } .tabbable { *zoom: 1; } .tabbable:before, .tabbable:after { display: table; line-height: 0; content: ""; } .tabbable:after { clear: both; } .tab-content { overflow: auto; } .tabs-below > .nav-tabs, .tabs-right > .nav-tabs, .tabs-left > .nav-tabs { border-bottom: 0; } .tab-content > .tab-pane, .pill-content > .pill-pane { display: none; } .tab-content > .active, .pill-content > .active { display: block; } .tabs-below > .nav-tabs { border-top: 1px solid #ddd; } .tabs-below > .nav-tabs > li { margin-top: -1px; margin-bottom: 0; } .tabs-below > .nav-tabs > li > a { -webkit-border-radius: 0 0 4px 4px; -moz-border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px; } .tabs-below > .nav-tabs > li > a:hover, .tabs-below > .nav-tabs > li > a:focus { border-top-color: #ddd; border-bottom-color: transparent; } .tabs-below > .nav-tabs > .active > a, .tabs-below > .nav-tabs > .active > a:hover, .tabs-below > .nav-tabs > .active > a:focus { border-color: transparent #ddd #ddd #ddd; } .tabs-left > .nav-tabs > li, .tabs-right > .nav-tabs > li { float: none; } .tabs-left > .nav-tabs > li > a, .tabs-right > .nav-tabs > li > a { min-width: 74px; margin-right: 0; margin-bottom: 3px; } .tabs-left > .nav-tabs { float: left; margin-right: 19px; border-right: 1px solid #ddd; } .tabs-left > .nav-tabs > li > a { margin-right: -1px; -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } .tabs-left > .nav-tabs > li > a:hover, .tabs-left > .nav-tabs > li > a:focus { border-color: #eeeeee #dddddd #eeeeee #eeeeee; } .tabs-left > .nav-tabs .active > a, .tabs-left > .nav-tabs .active > a:hover, .tabs-left > .nav-tabs .active > a:focus { border-color: #ddd transparent #ddd #ddd; *border-right-color: #ffffff; } .tabs-right > .nav-tabs { float: right; margin-left: 19px; border-left: 1px solid #ddd; } .tabs-right > .nav-tabs > li > a { margin-left: -1px; -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .tabs-right > .nav-tabs > li > a:hover, .tabs-right > .nav-tabs > li > a:focus { border-color: #eeeeee #eeeeee #eeeeee #dddddd; } .tabs-right > .nav-tabs .active > a, .tabs-right > .nav-tabs .active > a:hover, .tabs-right > .nav-tabs .active > a:focus { border-color: #ddd #ddd #ddd transparent; *border-left-color: #ffffff; } .nav > .disabled > a { color: #999999; } .nav > .disabled > a:hover, .nav > .disabled > a:focus { text-decoration: none; cursor: default; background-color: transparent; } .navbar { *position: relative; *z-index: 2; margin-bottom: 20px; overflow: visible; } .navbar-inner { min-height: 40px; padding-right: 20px; padding-left: 20px; background-color: #fafafa; background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); background-repeat: repeat-x; border: 1px solid #d4d4d4; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); *zoom: 1; -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); } .navbar-inner:before, .navbar-inner:after { display: table; line-height: 0; content: ""; } .navbar-inner:after { clear: both; } .navbar .container { width: auto; } .nav-collapse.collapse { height: auto; overflow: visible; } .navbar .brand { display: block; float: left; padding: 10px 20px 10px; margin-left: -20px; font-size: 20px; font-weight: 200; color: #777777; text-shadow: 0 1px 0 #ffffff; } .navbar .brand:hover, .navbar .brand:focus { text-decoration: none; } .navbar-text { margin-bottom: 0; line-height: 40px; color: #777777; } .navbar-link { color: #777777; } .navbar-link:hover, .navbar-link:focus { color: #333333; } .navbar .divider-vertical { height: 40px; margin: 0 9px; border-right: 1px solid #ffffff; border-left: 1px solid #f2f2f2; } .navbar .btn, .navbar .btn-group { margin-top: 5px; } .navbar .btn-group .btn, .navbar .input-prepend .btn, .navbar .input-append .btn, .navbar .input-prepend .btn-group, .navbar .input-append .btn-group { margin-top: 0; } .navbar-form { margin-bottom: 0; *zoom: 1; } .navbar-form:before, .navbar-form:after { display: table; line-height: 0; content: ""; } .navbar-form:after { clear: both; } .navbar-form input, .navbar-form select, .navbar-form .radio, .navbar-form .checkbox { margin-top: 5px; } .navbar-form input, .navbar-form select, .navbar-form .btn { display: inline-block; margin-bottom: 0; } .navbar-form input[type="image"], .navbar-form input[type="checkbox"], .navbar-form input[type="radio"] { margin-top: 3px; } .navbar-form .input-append, .navbar-form .input-prepend { margin-top: 5px; white-space: nowrap; } .navbar-form .input-append input, .navbar-form .input-prepend input { margin-top: 0; } .navbar-search { position: relative; float: left; margin-top: 5px; margin-bottom: 0; } .navbar-search .search-query { padding: 4px 14px; margin-bottom: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: 1; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px; } .navbar-static-top { position: static; margin-bottom: 0; } .navbar-static-top .navbar-inner { -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; margin-bottom: 0; } .navbar-fixed-top .navbar-inner, .navbar-static-top .navbar-inner { border-width: 0 0 1px; } .navbar-fixed-bottom .navbar-inner { border-width: 1px 0 0; } .navbar-fixed-top .navbar-inner, .navbar-fixed-bottom .navbar-inner { padding-right: 0; padding-left: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .navbar-static-top .container, .navbar-fixed-top .container, .navbar-fixed-bottom .container { width: 940px; } .navbar-fixed-top { top: 0; } .navbar-fixed-top .navbar-inner, .navbar-static-top .navbar-inner { -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); } .navbar-fixed-bottom { bottom: 0; } .navbar-fixed-bottom .navbar-inner { -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); } .navbar .nav { position: relative; left: 0; display: block; float: left; margin: 0 10px 0 0; } .navbar .nav.pull-right { float: right; margin-right: 0; } .navbar .nav > li { float: left; } .navbar .nav > li > a { float: none; padding: 10px 15px 10px; color: #777777; text-decoration: none; text-shadow: 0 1px 0 #ffffff; } .navbar .nav .dropdown-toggle .caret { margin-top: 8px; } .navbar .nav > li > a:focus, .navbar .nav > li > a:hover { color: hsl(210, 53%, 51%); text-decoration: none; background-color: transparent; } .navbar .nav > .active > a, .navbar .nav > .active > a:hover, .navbar .nav > .active > a:focus { color: #555555; text-decoration: none; background-color: #e5e5e5; -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); } .navbar .btn-navbar { display: none; float: right; padding: 7px 10px; margin-right: 5px; margin-left: 5px; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #ededed; *background-color: #e5e5e5; background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); background-repeat: repeat-x; border-color: #e5e5e5 #e5e5e5 #bfbfbf; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); } .navbar .btn-navbar:hover, .navbar .btn-navbar:focus, .navbar .btn-navbar:active, .navbar .btn-navbar.active, .navbar .btn-navbar.disabled, .navbar .btn-navbar[disabled] { color: #ffffff; background-color: #e5e5e5; *background-color: #d9d9d9; } .navbar .btn-navbar:active, .navbar .btn-navbar.active { background-color: #cccccc \9; } .navbar .btn-navbar .icon-bar { display: block; width: 18px; height: 2px; background-color: #f5f5f5; -webkit-border-radius: 1px; -moz-border-radius: 1px; border-radius: 1px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); } .btn-navbar .icon-bar + .icon-bar { margin-top: 3px; } .navbar .nav > li > .dropdown-menu:before { position: absolute; top: -7px; left: 9px; display: inline-block; border-right: 7px solid transparent; border-bottom: 7px solid #ccc; border-left: 7px solid transparent; border-bottom-color: rgba(0, 0, 0, 0.2); content: ''; } .navbar .nav > li > .dropdown-menu:after { position: absolute; top: -6px; left: 10px; display: inline-block; border-right: 6px solid transparent; border-bottom: 6px solid #ffffff; border-left: 6px solid transparent; content: ''; } .navbar-fixed-bottom .nav > li > .dropdown-menu:before { top: auto; bottom: -7px; border-top: 7px solid #ccc; border-bottom: 0; border-top-color: rgba(0, 0, 0, 0.2); } .navbar-fixed-bottom .nav > li > .dropdown-menu:after { top: auto; bottom: -6px; border-top: 6px solid #ffffff; border-bottom: 0; } .navbar .nav li.dropdown > a:hover .caret, .navbar .nav li.dropdown > a:focus .caret { border-top-color: #333333; border-bottom-color: #333333; } .navbar .nav li.dropdown.open > .dropdown-toggle, .navbar .nav li.dropdown.active > .dropdown-toggle, .navbar .nav li.dropdown.open.active > .dropdown-toggle { color: #555555; background-color: #e5e5e5; } .navbar .nav li.dropdown > .dropdown-toggle .caret { border-top-color: #777777; border-bottom-color: #777777; } .navbar .nav li.dropdown.open > .dropdown-toggle .caret, .navbar .nav li.dropdown.active > .dropdown-toggle .caret, .navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { border-top-color: #555555; border-bottom-color: #555555; } .navbar .pull-right > li > .dropdown-menu, .navbar .nav > li > .dropdown-menu.pull-right { right: 0; left: auto; } .navbar .pull-right > li > .dropdown-menu:before, .navbar .nav > li > .dropdown-menu.pull-right:before { right: 12px; left: auto; } .navbar .pull-right > li > .dropdown-menu:after, .navbar .nav > li > .dropdown-menu.pull-right:after { right: 13px; left: auto; } .navbar .pull-right > li > .dropdown-menu .dropdown-menu, .navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { right: 100%; left: auto; margin-right: -1px; margin-left: 0; -webkit-border-radius: 6px 0 6px 6px; -moz-border-radius: 6px 0 6px 6px; border-radius: 6px 0 6px 6px; } .navbar-inverse .navbar-inner { background-color: #1b1b1b; background-image: -moz-linear-gradient(top, #222222, #111111); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); background-image: -webkit-linear-gradient(top, #222222, #111111); background-image: -o-linear-gradient(top, #222222, #111111); background-image: linear-gradient(to bottom, #222222, #111111); background-repeat: repeat-x; border-color: #252525; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); } .navbar-inverse .brand, .navbar-inverse .nav > li > a { color: #999999; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .navbar-inverse .brand:hover, .navbar-inverse .nav > li > a:hover, .navbar-inverse .brand:focus, .navbar-inverse .nav > li > a:focus { color: #ffffff; } .navbar-inverse .brand { color: #999999; } .navbar-inverse .navbar-text { color: #999999; } .navbar-inverse .nav > li > a:focus, .navbar-inverse .nav > li > a:hover { color: #ffffff; background-color: transparent; } .navbar-inverse .nav .active > a, .navbar-inverse .nav .active > a:hover, .navbar-inverse .nav .active > a:focus { color: #ffffff; background-color: #111111; } .navbar-inverse .navbar-link { color: #999999; } .navbar-inverse .navbar-link:hover, .navbar-inverse .navbar-link:focus { color: #ffffff; } .navbar-inverse .divider-vertical { border-right-color: #222222; border-left-color: #111111; } .navbar-inverse .nav li.dropdown.open > .dropdown-toggle, .navbar-inverse .nav li.dropdown.active > .dropdown-toggle, .navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { color: #ffffff; background-color: #111111; } .navbar-inverse .nav li.dropdown > a:hover .caret, .navbar-inverse .nav li.dropdown > a:focus .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } .navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { border-top-color: #999999; border-bottom-color: #999999; } .navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, .navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, .navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } .navbar-inverse .navbar-search .search-query { color: #ffffff; background-color: #515151; border-color: #111111; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); -webkit-transition: none; -moz-transition: none; -o-transition: none; transition: none; } .navbar-inverse .navbar-search .search-query:-moz-placeholder { color: #cccccc; } .navbar-inverse .navbar-search .search-query:-ms-input-placeholder { color: #cccccc; } .navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { color: #cccccc; } .navbar-inverse .navbar-search .search-query:focus, .navbar-inverse .navbar-search .search-query.focused { padding: 5px 15px; color: #333333; text-shadow: 0 1px 0 #ffffff; background-color: #ffffff; border: 0; outline: 0; -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); } .navbar-inverse .btn-navbar { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #0e0e0e; *background-color: #040404; background-image: -moz-linear-gradient(top, #151515, #040404); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); background-image: -webkit-linear-gradient(top, #151515, #040404); background-image: -o-linear-gradient(top, #151515, #040404); background-image: linear-gradient(to bottom, #151515, #040404); background-repeat: repeat-x; border-color: #040404 #040404 #000000; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .navbar-inverse .btn-navbar:hover, .navbar-inverse .btn-navbar:focus, .navbar-inverse .btn-navbar:active, .navbar-inverse .btn-navbar.active, .navbar-inverse .btn-navbar.disabled, .navbar-inverse .btn-navbar[disabled] { color: #ffffff; background-color: #040404; *background-color: #000000; } .navbar-inverse .btn-navbar:active, .navbar-inverse .btn-navbar.active { background-color: #000000 \9; } .breadcrumb { padding: 8px 15px; margin: 0 0 20px; list-style: none; background-color: #f5f5f5; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .breadcrumb > li { display: inline-block; *display: inline; text-shadow: 0 1px 0 #ffffff; *zoom: 1; } .breadcrumb > li > .divider { padding: 0 5px; color: #ccc; } .breadcrumb > .active { color: #999999; } .pagination { margin: 20px 0; } .pagination ul { display: inline-block; *display: inline; margin-bottom: 0; margin-left: 0; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; *zoom: 1; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } .pagination ul > li { display: inline; } .pagination ul > li > a, .pagination ul > li > span { float: left; padding: 4px 12px; line-height: 20px; text-decoration: none; background-color: #ffffff; border: 1px solid #dddddd; border-left-width: 0; } .pagination ul > li > a:hover, .pagination ul > li > a:focus, .pagination ul > .active > a, .pagination ul > .active > span { background-color: #f5f5f5; } .pagination ul > .active > a, .pagination ul > .active > span { color: #999999; cursor: default; } .pagination ul > .disabled > span, .pagination ul > .disabled > a, .pagination ul > .disabled > a:hover, .pagination ul > .disabled > a:focus { color: #999999; cursor: default; background-color: transparent; } .pagination ul > li:first-child > a, .pagination ul > li:first-child > span { border-left-width: 1px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -moz-border-radius-topleft: 4px; } .pagination ul > li:last-child > a, .pagination ul > li:last-child > span { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-topright: 4px; -moz-border-radius-bottomright: 4px; } .pagination-centered { text-align: center; } .pagination-right { text-align: right; } .pagination-large ul > li > a, .pagination-large ul > li > span { padding: 11px 19px; font-size: 17.5px; } .pagination-large ul > li:first-child > a, .pagination-large ul > li:first-child > span { -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-bottomleft: 6px; -moz-border-radius-topleft: 6px; } .pagination-large ul > li:last-child > a, .pagination-large ul > li:last-child > span { -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; -moz-border-radius-topright: 6px; -moz-border-radius-bottomright: 6px; } .pagination-mini ul > li:first-child > a, .pagination-small ul > li:first-child > a, .pagination-mini ul > li:first-child > span, .pagination-small ul > li:first-child > span { -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-bottomleft: 3px; -moz-border-radius-topleft: 3px; } .pagination-mini ul > li:last-child > a, .pagination-small ul > li:last-child > a, .pagination-mini ul > li:last-child > span, .pagination-small ul > li:last-child > span { -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; -moz-border-radius-topright: 3px; -moz-border-radius-bottomright: 3px; } .pagination-small ul > li > a, .pagination-small ul > li > span { padding: 2px 10px; font-size: 11.9px; } .pagination-mini ul > li > a, .pagination-mini ul > li > span { padding: 0 6px; font-size: 10.5px; } .pager { margin: 20px 0; text-align: center; list-style: none; *zoom: 1; } .pager:before, .pager:after { display: table; line-height: 0; content: ""; } .pager:after { clear: both; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #fff; border: 1px solid #ddd; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #f5f5f5; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #999999; cursor: default; background-color: #fff; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; background-color: #000000; } .modal-backdrop.fade { opacity: 0; } .modal-backdrop, .modal-backdrop.fade.in { opacity: 0.8; filter: alpha(opacity=80); } .modal { position: fixed; top: 10%; left: 50%; z-index: 1050; width: 560px; margin-left: -280px; background-color: #ffffff; border: 1px solid #999; border: 1px solid rgba(0, 0, 0, 0.3); *border: 1px solid #999; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; outline: none; -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -webkit-background-clip: padding-box; -moz-background-clip: padding-box; background-clip: padding-box; } .modal.fade { top: -25%; -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; -moz-transition: opacity 0.3s linear, top 0.3s ease-out; -o-transition: opacity 0.3s linear, top 0.3s ease-out; transition: opacity 0.3s linear, top 0.3s ease-out; } .modal.fade.in { top: 10%; } .modal-header { padding: 9px 15px; border-bottom: 1px solid #eee; } .modal-header .close { margin-top: 2px; } .modal-header h3 { margin: 0; line-height: 30px; } .modal-body { position: relative; max-height: 400px; padding: 15px; overflow-y: auto; } .modal-form { margin-bottom: 0; } .modal-footer { padding: 14px 15px 15px; margin-bottom: 0; text-align: right; background-color: #f5f5f5; border-top: 1px solid #ddd; -webkit-border-radius: 0 0 6px 6px; -moz-border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px; *zoom: 1; -webkit-box-shadow: inset 0 1px 0 #ffffff; -moz-box-shadow: inset 0 1px 0 #ffffff; box-shadow: inset 0 1px 0 #ffffff; } .modal-footer:before, .modal-footer:after { display: table; line-height: 0; content: ""; } .modal-footer:after { clear: both; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } .tooltip { position: absolute; z-index: 1030; display: block; font-size: 11px; line-height: 1.4; opacity: 0; filter: alpha(opacity=0); visibility: visible; } .tooltip.in { opacity: 0.8; filter: alpha(opacity=80); } .tooltip.top { padding: 5px 0; margin-top: -3px; } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip.bottom { padding: 5px 0; margin-top: 3px; } .tooltip.left { padding: 0 5px; margin-left: -3px; } .tooltip-inner { max-width: 200px; padding: 8px; color: #ffffff; text-align: center; text-decoration: none; background-color: #000000; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-right-color: #000000; border-width: 5px 5px 5px 0; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-left-color: #000000; border-width: 5px 0 5px 5px; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .popover { position: absolute; top: 0; left: 0; z-index: 1010; display: none; max-width: 276px; padding: 1px; text-align: left; white-space: normal; background-color: #ffffff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; font-weight: normal; line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; -webkit-border-radius: 5px 5px 0 0; -moz-border-radius: 5px 5px 0 0; border-radius: 5px 5px 0 0; } .popover-title:empty { display: none; } .popover-content { padding: 9px 14px; } .popover .arrow, .popover .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover .arrow { border-width: 11px; } .popover .arrow:after { border-width: 10px; content: ""; } .popover.top .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999; border-top-color: rgba(0, 0, 0, 0.25); border-bottom-width: 0; } .popover.top .arrow:after { bottom: 1px; margin-left: -10px; border-top-color: #ffffff; border-bottom-width: 0; } .popover.right .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999; border-right-color: rgba(0, 0, 0, 0.25); border-left-width: 0; } .popover.right .arrow:after { bottom: -10px; left: 1px; border-right-color: #ffffff; border-left-width: 0; } .popover.bottom .arrow { top: -11px; left: 50%; margin-left: -11px; border-bottom-color: #999; border-bottom-color: rgba(0, 0, 0, 0.25); border-top-width: 0; } .popover.bottom .arrow:after { top: 1px; margin-left: -10px; border-bottom-color: #ffffff; border-top-width: 0; } .popover.left .arrow { top: 50%; right: -11px; margin-top: -11px; border-left-color: #999; border-left-color: rgba(0, 0, 0, 0.25); border-right-width: 0; } .popover.left .arrow:after { right: 1px; bottom: -10px; border-left-color: #ffffff; border-right-width: 0; } .thumbnails { margin-left: -20px; list-style: none; *zoom: 1; } .thumbnails:before, .thumbnails:after { display: table; line-height: 0; content: ""; } .thumbnails:after { clear: both; } .row-fluid .thumbnails { margin-left: 0; } .thumbnails > li { float: left; margin-bottom: 20px; margin-left: 20px; } .thumbnail { display: block; padding: 4px; line-height: 20px; border: 1px solid #ddd; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); -webkit-transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } a.thumbnail:hover, a.thumbnail:focus { border-color: #0088cc; -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); } .thumbnail > img { display: block; max-width: 100%; margin-right: auto; margin-left: auto; } .thumbnail .caption { padding: 9px; color: #555555; } .media, .media-body { overflow: hidden; *overflow: visible; zoom: 1; } .media, .media .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media-object { display: block; } .media-heading { margin: 0 0 5px; } .media > .pull-left { margin-right: 10px; } .media > .pull-right { margin-left: 10px; } .media-list { margin-left: 0; list-style: none; } .label, .badge { display: inline-block; padding: 2px 4px; font-size: 11.844px; font-weight: bold; line-height: 14px; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); white-space: nowrap; vertical-align: baseline; background-color: #999999; } .label { -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .badge { padding-right: 9px; padding-left: 9px; -webkit-border-radius: 9px; -moz-border-radius: 9px; border-radius: 9px; } .label:empty, .badge:empty { display: none; } a.label:hover, a.label:focus, a.badge:hover, a.badge:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label-important, .badge-important { background-color: #b94a48; } .label-important[href], .badge-important[href] { background-color: #953b39; } .label-warning, .badge-warning { background-color: #f89406; } .label-warning[href], .badge-warning[href] { background-color: #c67605; } .label-success, .badge-success { background-color: #468847; } .label-success[href], .badge-success[href] { background-color: #356635; } .label-info, .badge-info { background-color: #3a87ad; } .label-info[href], .badge-info[href] { background-color: #2d6987; } .label-inverse, .badge-inverse { background-color: #333333; } .label-inverse[href], .badge-inverse[href] { background-color: #1a1a1a; } .btn .label, .btn .badge { position: relative; top: -1px; } .btn-mini .label, .btn-mini .badge { top: 0; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-moz-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-ms-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 20px; margin-bottom: 20px; overflow: hidden; background-color: #f7f7f7; background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); background-repeat: repeat-x; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress .bar { float: left; width: 0; height: 100%; font-size: 12px; color: #ffffff; text-align: center; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #0e90d2; background-image: -moz-linear-gradient(top, #149bdf, #0480be); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); background-image: -webkit-linear-gradient(top, #149bdf, #0480be); background-image: -o-linear-gradient(top, #149bdf, #0480be); background-image: linear-gradient(to bottom, #149bdf, #0480be); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-transition: width 0.6s ease; -moz-transition: width 0.6s ease; -o-transition: width 0.6s ease; transition: width 0.6s ease; } .progress .bar + .bar { -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); } .progress-striped .bar { background-color: #149bdf; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -webkit-background-size: 40px 40px; -moz-background-size: 40px 40px; -o-background-size: 40px 40px; background-size: 40px 40px; } .progress.active .bar { -webkit-animation: progress-bar-stripes 2s linear infinite; -moz-animation: progress-bar-stripes 2s linear infinite; -ms-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-danger .bar, .progress .bar-danger { background-color: #dd514c; background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); } .progress-danger.progress-striped .bar, .progress-striped .bar-danger { background-color: #ee5f5b; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-success .bar, .progress .bar-success { background-color: #5eb95e; background-image: -moz-linear-gradient(top, #62c462, #57a957); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); background-image: -webkit-linear-gradient(top, #62c462, #57a957); background-image: -o-linear-gradient(top, #62c462, #57a957); background-image: linear-gradient(to bottom, #62c462, #57a957); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); } .progress-success.progress-striped .bar, .progress-striped .bar-success { background-color: #62c462; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-info .bar, .progress .bar-info { background-color: #4bb1cf; background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); background-image: -o-linear-gradient(top, #5bc0de, #339bb9); background-image: linear-gradient(to bottom, #5bc0de, #339bb9); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); } .progress-info.progress-striped .bar, .progress-striped .bar-info { background-color: #5bc0de; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-warning .bar, .progress .bar-warning { background-color: #faa732; background-image: -moz-linear-gradient(top, #fbb450, #f89406); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); background-image: -webkit-linear-gradient(top, #fbb450, #f89406); background-image: -o-linear-gradient(top, #fbb450, #f89406); background-image: linear-gradient(to bottom, #fbb450, #f89406); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); } .progress-warning.progress-striped .bar, .progress-striped .bar-warning { background-color: #fbb450; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .accordion { margin-bottom: 20px; } .accordion-group { margin-bottom: 2px; border: 1px solid #e5e5e5; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .accordion-heading { border-bottom: 0; } .accordion-heading .accordion-toggle { display: block; padding: 8px 15px; } .accordion-toggle { cursor: pointer; } .accordion-inner { padding: 9px 15px; border-top: 1px solid #e5e5e5; } .carousel { position: relative; margin-bottom: 20px; line-height: 1; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: 0.6s ease-in-out left; -moz-transition: 0.6s ease-in-out left; -o-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; line-height: 1; } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 40%; left: 15px; width: 40px; height: 40px; margin-top: -20px; font-size: 60px; font-weight: 100; line-height: 30px; color: #ffffff; text-align: center; background: #222222; border: 3px solid #ffffff; -webkit-border-radius: 23px; -moz-border-radius: 23px; border-radius: 23px; opacity: 0.5; filter: alpha(opacity=50); } .carousel-control.right { right: 15px; left: auto; } .carousel-control:hover, .carousel-control:focus { color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-indicators { position: absolute; top: 15px; right: 15px; z-index: 5; margin: 0; list-style: none; } .carousel-indicators li { display: block; float: left; width: 10px; height: 10px; margin-left: 5px; text-indent: -999px; background-color: #ccc; background-color: rgba(255, 255, 255, 0.25); border-radius: 5px; } .carousel-indicators .active { background-color: #fff; } .carousel-caption { position: absolute; right: 0; bottom: 0; left: 0; padding: 15px; background: #333333; background: rgba(0, 0, 0, 0.75); } .carousel-caption h4, .carousel-caption p { line-height: 20px; color: #ffffff; } .carousel-caption h4 { margin: 0 0 5px; } .carousel-caption p { margin-bottom: 0; } .hero-unit { padding: 60px; margin-bottom: 30px; font-size: 18px; font-weight: 200; line-height: 30px; color: inherit; background-color: #eeeeee; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .hero-unit h1 { margin-bottom: 0; font-size: 60px; line-height: 1; letter-spacing: -1px; color: inherit; } .hero-unit li { line-height: 30px; } .pull-right { float: right; } .pull-left { float: left; } .hide { display: none; } .show { display: block; } .invisible { visibility: hidden; } .affix { position: fixed; } ================================================ FILE: public/libs/bootstrap/js/bootstrap.js ================================================ /* =================================================== * bootstrap-transition.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#transitions * =================================================== * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ========================================================== */ !function ($) { "use strict"; // jshint ;_; /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) * ======================================================= */ $(function () { $.support.transition = (function () { var transitionEnd = (function () { var el = document.createElement('bootstrap') , transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd otransitionend', 'transition': 'transitionend' } , name for (name in transEndEventNames) { if (el.style[name] !== undefined) { return transEndEventNames[name] } } }()) return transitionEnd && { end: transitionEnd } })() }) }(window.jQuery); /* ========================================================== * bootstrap-alert.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#alerts * ========================================================== * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ========================================================== */ !function ($) { "use strict"; // jshint ;_; /* ALERT CLASS DEFINITION * ====================== */ var dismiss = '[data-dismiss="alert"]' , Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.prototype.close = function (e) { var $this = $(this) , selector = $this.attr('data-target') , $parent if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 } $parent = $(selector) e && e.preventDefault() $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) $parent.trigger(e = $.Event('close')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { $parent .trigger('closed') .remove() } $.support.transition && $parent.hasClass('fade') ? $parent.on($.support.transition.end, removeElement) : removeElement() } /* ALERT PLUGIN DEFINITION * ======================= */ var old = $.fn.alert $.fn.alert = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('alert') if (!data) $this.data('alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } $.fn.alert.Constructor = Alert /* ALERT NO CONFLICT * ================= */ $.fn.alert.noConflict = function () { $.fn.alert = old return this } /* ALERT DATA-API * ============== */ $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) }(window.jQuery); /* ============================================================ * bootstrap-button.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#buttons * ============================================================ * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================ */ !function ($) { "use strict"; // jshint ;_; /* BUTTON PUBLIC CLASS DEFINITION * ============================== */ var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, $.fn.button.defaults, options) } Button.prototype.setState = function (state) { var d = 'disabled' , $el = this.$element , data = $el.data() , val = $el.is('input') ? 'val' : 'html' state = state + 'Text' data.resetText || $el.data('resetText', $el[val]()) $el[val](data[state] || this.options[state]) // push to event loop to allow forms to submit setTimeout(function () { state == 'loadingText' ? $el.addClass(d).attr(d, d) : $el.removeClass(d).removeAttr(d) }, 0) } Button.prototype.toggle = function () { var $parent = this.$element.closest('[data-toggle="buttons-radio"]') $parent && $parent .find('.active') .removeClass('active') this.$element.toggleClass('active') } /* BUTTON PLUGIN DEFINITION * ======================== */ var old = $.fn.button $.fn.button = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('button') , options = typeof option == 'object' && option if (!data) $this.data('button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } $.fn.button.defaults = { loadingText: 'loading...' } $.fn.button.Constructor = Button /* BUTTON NO CONFLICT * ================== */ $.fn.button.noConflict = function () { $.fn.button = old return this } /* BUTTON DATA-API * =============== */ $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') $btn.button('toggle') }) }(window.jQuery); /* ========================================================== * bootstrap-carousel.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#carousel * ========================================================== * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ========================================================== */ !function ($) { "use strict"; // jshint ;_; /* CAROUSEL CLASS DEFINITION * ========================= */ var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.options.pause == 'hover' && this.$element .on('mouseenter', $.proxy(this.pause, this)) .on('mouseleave', $.proxy(this.cycle, this)) } Carousel.prototype = { cycle: function (e) { if (!e) this.paused = false if (this.interval) clearInterval(this.interval); this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this }, getActiveIndex: function () { this.$active = this.$element.find('.item.active') this.$items = this.$active.parent().children() return this.$items.index(this.$active) }, to: function (pos) { var activeIndex = this.getActiveIndex() , that = this if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) { return this.$element.one('slid', function () { that.to(pos) }) } if (activeIndex == pos) { return this.pause().cycle() } return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) }, pause: function (e) { if (!e) this.paused = true if (this.$element.find('.next, .prev').length && $.support.transition.end) { this.$element.trigger($.support.transition.end) this.cycle(true) } clearInterval(this.interval) this.interval = null return this }, next: function () { if (this.sliding) return return this.slide('next') }, prev: function () { if (this.sliding) return return this.slide('prev') }, slide: function (type, next) { var $active = this.$element.find('.item.active') , $next = next || $active[type]() , isCycling = this.interval , direction = type == 'next' ? 'left' : 'right' , fallback = type == 'next' ? 'first' : 'last' , that = this , e this.sliding = true isCycling && this.pause() $next = $next.length ? $next : this.$element.find('.item')[fallback]() e = $.Event('slide', { relatedTarget: $next[0], direction: direction }) if ($next.hasClass('active')) return if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') this.$element.one('slid', function () { var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) $nextIndicator && $nextIndicator.addClass('active') }) } if ($.support.transition && this.$element.hasClass('slide')) { this.$element.trigger(e) if (e.isDefaultPrevented()) return $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) this.$element.one($.support.transition.end, function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger('slid') }, 0) }) } else { this.$element.trigger(e) if (e.isDefaultPrevented()) return $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger('slid') } isCycling && this.cycle() return this } } /* CAROUSEL PLUGIN DEFINITION * ========================== */ var old = $.fn.carousel $.fn.carousel = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('carousel') , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) , action = typeof option == 'string' ? option : options.slide if (!data) $this.data('carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } $.fn.carousel.defaults = { interval: 5000, pause: 'hover' } $.fn.carousel.Constructor = Carousel /* CAROUSEL NO CONFLICT * ==================== */ $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } /* CAROUSEL DATA-API * ================= */ $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { var $this = $(this), href , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 , options = $.extend({}, $target.data(), $this.data()) , slideIndex $target.carousel(options) if (slideIndex = $this.attr('data-slide-to')) { $target.data('carousel').pause().to(slideIndex).cycle() } e.preventDefault() }) }(window.jQuery); /* ============================================================= * bootstrap-collapse.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#collapse * ============================================================= * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================ */ !function ($) { "use strict"; // jshint ;_; /* COLLAPSE PUBLIC CLASS DEFINITION * ================================ */ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, $.fn.collapse.defaults, options) if (this.options.parent) { this.$parent = $(this.options.parent) } this.options.toggle && this.toggle() } Collapse.prototype = { constructor: Collapse, dimension: function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' }, show: function () { var dimension , scroll , actives , hasData if (this.transitioning || this.$element.hasClass('in')) return dimension = this.dimension() scroll = $.camelCase(['scroll', dimension].join('-')) actives = this.$parent && this.$parent.find('> .accordion-group > .in') if (actives && actives.length) { hasData = actives.data('collapse') if (hasData && hasData.transitioning) return actives.collapse('hide') hasData || actives.data('collapse', null) } this.$element[dimension](0) this.transition('addClass', $.Event('show'), 'shown') $.support.transition && this.$element[dimension](this.$element[0][scroll]) }, hide: function () { var dimension if (this.transitioning || !this.$element.hasClass('in')) return dimension = this.dimension() this.reset(this.$element[dimension]()) this.transition('removeClass', $.Event('hide'), 'hidden') this.$element[dimension](0) }, reset: function (size) { var dimension = this.dimension() this.$element .removeClass('collapse') [dimension](size || 'auto') [0].offsetWidth this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') return this }, transition: function (method, startEvent, completeEvent) { var that = this , complete = function () { if (startEvent.type == 'show') that.reset() that.transitioning = 0 that.$element.trigger(completeEvent) } this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return this.transitioning = 1 this.$element[method]('in') $.support.transition && this.$element.hasClass('collapse') ? this.$element.one($.support.transition.end, complete) : complete() }, toggle: function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } } /* COLLAPSE PLUGIN DEFINITION * ========================== */ var old = $.fn.collapse $.fn.collapse = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('collapse') , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) if (!data) $this.data('collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } $.fn.collapse.defaults = { toggle: true } $.fn.collapse.Constructor = Collapse /* COLLAPSE NO CONFLICT * ==================== */ $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } /* COLLAPSE DATA-API * ================= */ $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { var $this = $(this), href , target = $this.attr('data-target') || e.preventDefault() || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 , option = $(target).data('collapse') ? 'toggle' : $this.data() $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') $(target).collapse(option) }) }(window.jQuery); /* ============================================================ * bootstrap-dropdown.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#dropdowns * ============================================================ * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================ */ !function ($) { "use strict"; // jshint ;_; /* DROPDOWN CLASS DEFINITION * ========================= */ var toggle = '[data-toggle=dropdown]' , Dropdown = function (element) { var $el = $(element).on('click.dropdown.data-api', this.toggle) $('html').on('click.dropdown.data-api', function () { $el.parent().removeClass('open') }) } Dropdown.prototype = { constructor: Dropdown, toggle: function (e) { var $this = $(this) , $parent , isActive if ($this.is('.disabled, :disabled')) return $parent = getParent($this) isActive = $parent.hasClass('open') clearMenus() if (!isActive) { $parent.toggleClass('open') } $this.focus() return false }, keydown: function (e) { var $this , $items , $active , $parent , isActive , index if (!/(38|40|27)/.test(e.keyCode)) return $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return $parent = getParent($this) isActive = $parent.hasClass('open') if (!isActive || (isActive && e.keyCode == 27)) { if (e.which == 27) $parent.find(toggle).focus() return $this.click() } $items = $('[role=menu] li:not(.divider):visible a', $parent) if (!$items.length) return index = $items.index($items.filter(':focus')) if (e.keyCode == 38 && index > 0) index-- // up if (e.keyCode == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items .eq(index) .focus() } } function clearMenus() { $(toggle).each(function () { getParent($(this)).removeClass('open') }) } function getParent($this) { var selector = $this.attr('data-target') , $parent if (!selector) { selector = $this.attr('href') selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 } $parent = selector && $(selector) if (!$parent || !$parent.length) $parent = $this.parent() return $parent } /* DROPDOWN PLUGIN DEFINITION * ========================== */ var old = $.fn.dropdown $.fn.dropdown = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('dropdown') if (!data) $this.data('dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } $.fn.dropdown.Constructor = Dropdown /* DROPDOWN NO CONFLICT * ==================== */ $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this } /* APPLY TO STANDARD DROPDOWN ELEMENTS * =================================== */ $(document) .on('click.dropdown.data-api', clearMenus) .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.dropdown-menu', function (e) { e.stopPropagation() }) .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) .on('keydown.dropdown.data-api', toggle + ', [role=menu]', Dropdown.prototype.keydown) }(window.jQuery); /* ========================================================= * bootstrap-modal.js v2.3.1 * http://twitter.github.com/bootstrap/javascript.html#modals * ========================================================= * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ========================================================= */ !function ($) { "use strict"; // jshint ;_; /* MODAL CLASS DEFINITION * ====================== */ var Modal = function (element, options) { this.options = options this.$element = $(element) .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) this.options.remote && this.$element.find('.modal-body').load(this.options.remote) } Modal.prototype = { constructor: Modal, toggle: function () { return this[!this.isShown ? 'show' : 'hide']() }, show: function () { var that = this , e = $.Event('show') this.$element.trigger(e) if (this.isShown || e.isDefaultPrevented()) return this.isShown = true this.escape() this.backdrop(function () { var transition = $.support.transition && that.$element.hasClass('fade') if (!that.$element.parent().length) { that.$element.appendTo(document.body) //don't move modals dom position } that.$element.show() if (transition) { that.$element[0].offsetWidth // force reflow } that.$element .addClass('in') .attr('aria-hidden', false) that.enforceFocus() transition ? that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) : that.$element.focus().trigger('shown') }) }, hide: function (e) { e && e.preventDefault() var that = this e = $.Event('hide') this.$element.trigger(e) if (!this.isShown || e.isDefaultPrevented()) return this.isShown = false this.escape() $(document).off('focusin.modal') this.$element .removeClass('in') .attr('aria-hidden', true) $.support.transition && this.$element.hasClass('fade') ? this.hideWithTransition() : this.hideModal() }, enforceFocus: function () { var that = this $(document).on('focusin.modal', function (e) { if (that.$element[0] !== e.target && !that.$element.has(e.target).length) { that.$element.focus() } }) }, escape: function () { var that = this if (this.isShown && this.options.keyboard) { this.$element.on('keyup.dismiss.modal', function (e) { e.which == 27 && that.hide() }) } else if (!this.isShown) { this.$element.off('keyup.dismiss.modal') } }, hideWithTransition: function () { var that = this , timeout = setTimeout(function () { that.$element.off($.support.transition.end) that.hideModal() }, 500) this.$element.one($.support.transition.end, function () { clearTimeout(timeout) that.hideModal() }) }, hideModal: function () { var that = this this.$element.hide() this.backdrop(function () { that.removeBackdrop() that.$element.trigger('hidden') }) }, removeBackdrop: function () { this.$backdrop && this.$backdrop.remove() this.$backdrop = null }, backdrop: function (callback) { var that = this , animate = this.$element.hasClass('fade') ? 'fade' : '' if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate this.$backdrop = $('' ].join('')).appendTo($body); this.$win.on('click', '[role=save]', function(){ self.$win.find('form').submit(); }).on('submit', 'form', function(){ var $el = $(this); var title = $el.find('[name=title]').val(); var link = $el.find('[name=link]').val(); self.$win.modal('hide'); var cm = self.editor.codemirror; var stat = getState(cm); _replaceSelection(cm, stat.link, '['+ title +']('+ link +')'); $el.find('[name=title]').val(''); $el.find('[name=link]').val('http://'); return false; }); }; ToolLink.prototype.bind = function(editor){ this.editor = editor; this.$win.modal('show'); }; //图片上传工具 var ToolImage = function(){ var self = this; this.$win = $([ '' ].join('')).appendTo($body); this.$upload = this.$win.find('.upload-img').css({ height: 50, padding: '60px 0', textAlign: 'center', border: '4px dashed#ddd' }); this.$uploadBtn = this.$upload.find('.button').css({ width: 86, height: 40, margin: '0 auto' }); this.$uploadTip = this.$upload.find('.tip').hide(); this.file = false; var _csrf = $('[name=_csrf]').val(); this.uploader = WebUploader.create({ swf: '/public/libs/webuploader/Uploader.swf', server: '/upload?_csrf=' + _csrf, pick: this.$uploadBtn[0], paste: document.body, dnd: this.$upload[0], auto: true, fileSingleSizeLimit: 1 * 1024 * 1024, //sendAsBinary: true, // 只允许选择图片文件。 accept: { title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' } }); this.uploader.on('beforeFileQueued', function(file){ if(self.file !== false || !self.editor){ return false; } self.showFile(file); }); this.uploader.on('uploadProgress', function(file, percentage){ // console.log(percentage); self.showProgress(file, percentage * 100); }); this.uploader.on('uploadSuccess', function(file, res){ if(res.success){ self.$win.modal('hide'); var cm = self.editor.codemirror; var stat = getState(cm); _replaceSelection(cm, stat.image, '!['+ file.name +']('+ res.url +')'); } else{ self.removeFile(); self.showError(res.msg || '服务器走神了,上传失败'); } }); this.uploader.on('uploadComplete', function(file){ self.uploader.removeFile(file); self.removeFile(); }); this.uploader.on('error', function(type){ self.removeFile(); switch(type){ case 'Q_EXCEED_SIZE_LIMIT': case 'F_EXCEED_SIZE': self.showError('文件太大了, 不能超过1MB'); break; case 'Q_TYPE_DENIED': self.showError('只能上传图片'); break; default: self.showError('发生未知错误'); } }); this.uploader.on('uploadError', function(){ self.removeFile(); self.showError('服务器走神了,上传失败'); }); }; ToolImage.prototype.removeFile = function(){ //var self = this; this.file = false; this.$uploadBtn.show(); this.$uploadTip.hide(); }; ToolImage.prototype.showFile = function(file){ //var self = this; this.file = file; this.$uploadBtn.hide(); this.$uploadTip.html('正在上传: ' + file.name).show(); this.hideError(); }; ToolImage.prototype.showError = function(error){ this.$upload.find('.alert-error').html(error).show(); }; ToolImage.prototype.hideError = function(error){ this.$upload.find('.alert-error').hide(); }; ToolImage.prototype.showProgress = function(file, percentage){ this.$uploadTip .html('正在上传: ' + file.name + ' ' + percentage + '%') .show(); }; ToolImage.prototype.bind = function(editor){ this.editor = editor; this.$win.modal('show'); }; var toolImage = new ToolImage(); var toolLink = new ToolLink(); replaceTool('image', function(editor){ toolImage.bind(editor); }); replaceTool('link', function(editor){ toolLink.bind(editor); }); //当编辑器取得焦点时,绑定 toolImage; var createToolbar = Editor.prototype.createToolbar; Editor.prototype.createToolbar = function(items){ createToolbar.call(this, items); var self = this; $(self.codemirror.display.input).on('focus', function(){ toolImage.editor = self; }); }; //追加内容 Editor.prototype.push = function(txt){ var cm = this.codemirror; var line = cm.lastLine(); cm.setLine(line, cm.getLine(line) + txt); }; })(window.Editor, window.markdownit, window.WebUploader); ================================================ FILE: public/libs/font-awesome/css/font-awesome.css ================================================ /*! * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ /* FONT PATH * -------------------------- */ @font-face { font-family: 'FontAwesome'; src: url('/public/libs/font-awesome/fonts/fontawesome-webfont.eot?v=4.2.0'); src: url('/public/libs/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('/public/libs/font-awesome/fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('/public/libs/font-awesome/fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('/public/libs/font-awesome/fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } .fa { display: inline-block; font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* makes the font 33% larger relative to the icon container */ .fa-lg { font-size: 1.33333333em; line-height: 0.75em; vertical-align: -15%; } .fa-2x { font-size: 2em; } .fa-3x { font-size: 3em; } .fa-4x { font-size: 4em; } .fa-5x { font-size: 5em; } .fa-fw { width: 1.28571429em; text-align: center; } .fa-ul { padding-left: 0; margin-left: 2.14285714em; list-style-type: none; } .fa-ul > li { position: relative; } .fa-li { position: absolute; left: -2.14285714em; width: 2.14285714em; top: 0.14285714em; text-align: center; } .fa-li.fa-lg { left: -1.85714286em; } .fa-border { padding: .2em .25em .15em; border: solid 0.08em #eeeeee; border-radius: .1em; } .pull-right { float: right; } .pull-left { float: left; } .fa.pull-left { margin-right: .3em; } .fa.pull-right { margin-left: .3em; } .fa-spin { -webkit-animation: fa-spin 2s infinite linear; animation: fa-spin 2s infinite linear; } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } .fa-rotate-90 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); } .fa-rotate-180 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } .fa-rotate-270 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } .fa-flip-horizontal { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); -webkit-transform: scale(-1, 1); -ms-transform: scale(-1, 1); transform: scale(-1, 1); } .fa-flip-vertical { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); -webkit-transform: scale(1, -1); -ms-transform: scale(1, -1); transform: scale(1, -1); } :root .fa-rotate-90, :root .fa-rotate-180, :root .fa-rotate-270, :root .fa-flip-horizontal, :root .fa-flip-vertical { filter: none; } .fa-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle; } .fa-stack-1x, .fa-stack-2x { position: absolute; left: 0; width: 100%; text-align: center; } .fa-stack-1x { line-height: inherit; } .fa-stack-2x { font-size: 2em; } .fa-inverse { color: #ffffff; } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .fa-glass:before { content: "\f000"; } .fa-music:before { content: "\f001"; } .fa-search:before { content: "\f002"; } .fa-envelope-o:before { content: "\f003"; } .fa-heart:before { content: "\f004"; } .fa-star:before { content: "\f005"; } .fa-star-o:before { content: "\f006"; } .fa-user:before { content: "\f007"; } .fa-film:before { content: "\f008"; } .fa-th-large:before { content: "\f009"; } .fa-th:before { content: "\f00a"; } .fa-th-list:before { content: "\f00b"; } .fa-check:before { content: "\f00c"; } .fa-remove:before, .fa-close:before, .fa-times:before { content: "\f00d"; } .fa-search-plus:before { content: "\f00e"; } .fa-search-minus:before { content: "\f010"; } .fa-power-off:before { content: "\f011"; } .fa-signal:before { content: "\f012"; } .fa-gear:before, .fa-cog:before { content: "\f013"; } .fa-trash-o:before { content: "\f014"; } .fa-home:before { content: "\f015"; } .fa-file-o:before { content: "\f016"; } .fa-clock-o:before { content: "\f017"; } .fa-road:before { content: "\f018"; } .fa-download:before { content: "\f019"; } .fa-arrow-circle-o-down:before { content: "\f01a"; } .fa-arrow-circle-o-up:before { content: "\f01b"; } .fa-inbox:before { content: "\f01c"; } .fa-play-circle-o:before { content: "\f01d"; } .fa-rotate-right:before, .fa-repeat:before { content: "\f01e"; } .fa-refresh:before { content: "\f021"; } .fa-list-alt:before { content: "\f022"; } .fa-lock:before { content: "\f023"; } .fa-flag:before { content: "\f024"; } .fa-headphones:before { content: "\f025"; } .fa-volume-off:before { content: "\f026"; } .fa-volume-down:before { content: "\f027"; } .fa-volume-up:before { content: "\f028"; } .fa-qrcode:before { content: "\f029"; } .fa-barcode:before { content: "\f02a"; } .fa-tag:before { content: "\f02b"; } .fa-tags:before { content: "\f02c"; } .fa-book:before { content: "\f02d"; } .fa-bookmark:before { content: "\f02e"; } .fa-print:before { content: "\f02f"; } .fa-camera:before { content: "\f030"; } .fa-font:before { content: "\f031"; } .fa-bold:before { content: "\f032"; } .fa-italic:before { content: "\f033"; } .fa-text-height:before { content: "\f034"; } .fa-text-width:before { content: "\f035"; } .fa-align-left:before { content: "\f036"; } .fa-align-center:before { content: "\f037"; } .fa-align-right:before { content: "\f038"; } .fa-align-justify:before { content: "\f039"; } .fa-list:before { content: "\f03a"; } .fa-dedent:before, .fa-outdent:before { content: "\f03b"; } .fa-indent:before { content: "\f03c"; } .fa-video-camera:before { content: "\f03d"; } .fa-photo:before, .fa-image:before, .fa-picture-o:before { content: "\f03e"; } .fa-pencil:before { content: "\f040"; } .fa-map-marker:before { content: "\f041"; } .fa-adjust:before { content: "\f042"; } .fa-tint:before { content: "\f043"; } .fa-edit:before, .fa-pencil-square-o:before { content: "\f044"; } .fa-share-square-o:before { content: "\f045"; } .fa-check-square-o:before { content: "\f046"; } .fa-arrows:before { content: "\f047"; } .fa-step-backward:before { content: "\f048"; } .fa-fast-backward:before { content: "\f049"; } .fa-backward:before { content: "\f04a"; } .fa-play:before { content: "\f04b"; } .fa-pause:before { content: "\f04c"; } .fa-stop:before { content: "\f04d"; } .fa-forward:before { content: "\f04e"; } .fa-fast-forward:before { content: "\f050"; } .fa-step-forward:before { content: "\f051"; } .fa-eject:before { content: "\f052"; } .fa-chevron-left:before { content: "\f053"; } .fa-chevron-right:before { content: "\f054"; } .fa-plus-circle:before { content: "\f055"; } .fa-minus-circle:before { content: "\f056"; } .fa-times-circle:before { content: "\f057"; } .fa-check-circle:before { content: "\f058"; } .fa-question-circle:before { content: "\f059"; } .fa-info-circle:before { content: "\f05a"; } .fa-crosshairs:before { content: "\f05b"; } .fa-times-circle-o:before { content: "\f05c"; } .fa-check-circle-o:before { content: "\f05d"; } .fa-ban:before { content: "\f05e"; } .fa-arrow-left:before { content: "\f060"; } .fa-arrow-right:before { content: "\f061"; } .fa-arrow-up:before { content: "\f062"; } .fa-arrow-down:before { content: "\f063"; } .fa-mail-forward:before, .fa-share:before { content: "\f064"; } .fa-expand:before { content: "\f065"; } .fa-compress:before { content: "\f066"; } .fa-plus:before { content: "\f067"; } .fa-minus:before { content: "\f068"; } .fa-asterisk:before { content: "\f069"; } .fa-exclamation-circle:before { content: "\f06a"; } .fa-gift:before { content: "\f06b"; } .fa-leaf:before { content: "\f06c"; } .fa-fire:before { content: "\f06d"; } .fa-eye:before { content: "\f06e"; } .fa-eye-slash:before { content: "\f070"; } .fa-warning:before, .fa-exclamation-triangle:before { content: "\f071"; } .fa-plane:before { content: "\f072"; } .fa-calendar:before { content: "\f073"; } .fa-random:before { content: "\f074"; } .fa-comment:before { content: "\f075"; } .fa-magnet:before { content: "\f076"; } .fa-chevron-up:before { content: "\f077"; } .fa-chevron-down:before { content: "\f078"; } .fa-retweet:before { content: "\f079"; } .fa-shopping-cart:before { content: "\f07a"; } .fa-folder:before { content: "\f07b"; } .fa-folder-open:before { content: "\f07c"; } .fa-arrows-v:before { content: "\f07d"; } .fa-arrows-h:before { content: "\f07e"; } .fa-bar-chart-o:before, .fa-bar-chart:before { content: "\f080"; } .fa-twitter-square:before { content: "\f081"; } .fa-facebook-square:before { content: "\f082"; } .fa-camera-retro:before { content: "\f083"; } .fa-key:before { content: "\f084"; } .fa-gears:before, .fa-cogs:before { content: "\f085"; } .fa-comments:before { content: "\f086"; } .fa-thumbs-o-up:before { content: "\f087"; } .fa-thumbs-o-down:before { content: "\f088"; } .fa-star-half:before { content: "\f089"; } .fa-heart-o:before { content: "\f08a"; } .fa-sign-out:before { content: "\f08b"; } .fa-linkedin-square:before { content: "\f08c"; } .fa-thumb-tack:before { content: "\f08d"; } .fa-external-link:before { content: "\f08e"; } .fa-sign-in:before { content: "\f090"; } .fa-trophy:before { content: "\f091"; } .fa-github-square:before { content: "\f092"; } .fa-upload:before { content: "\f093"; } .fa-lemon-o:before { content: "\f094"; } .fa-phone:before { content: "\f095"; } .fa-square-o:before { content: "\f096"; } .fa-bookmark-o:before { content: "\f097"; } .fa-phone-square:before { content: "\f098"; } .fa-twitter:before { content: "\f099"; } .fa-facebook:before { content: "\f09a"; } .fa-github:before { content: "\f09b"; } .fa-unlock:before { content: "\f09c"; } .fa-credit-card:before { content: "\f09d"; } .fa-rss:before { content: "\f09e"; } .fa-hdd-o:before { content: "\f0a0"; } .fa-bullhorn:before { content: "\f0a1"; } .fa-bell:before { content: "\f0f3"; } .fa-certificate:before { content: "\f0a3"; } .fa-hand-o-right:before { content: "\f0a4"; } .fa-hand-o-left:before { content: "\f0a5"; } .fa-hand-o-up:before { content: "\f0a6"; } .fa-hand-o-down:before { content: "\f0a7"; } .fa-arrow-circle-left:before { content: "\f0a8"; } .fa-arrow-circle-right:before { content: "\f0a9"; } .fa-arrow-circle-up:before { content: "\f0aa"; } .fa-arrow-circle-down:before { content: "\f0ab"; } .fa-globe:before { content: "\f0ac"; } .fa-wrench:before { content: "\f0ad"; } .fa-tasks:before { content: "\f0ae"; } .fa-filter:before { content: "\f0b0"; } .fa-briefcase:before { content: "\f0b1"; } .fa-arrows-alt:before { content: "\f0b2"; } .fa-group:before, .fa-users:before { content: "\f0c0"; } .fa-chain:before, .fa-link:before { content: "\f0c1"; } .fa-cloud:before { content: "\f0c2"; } .fa-flask:before { content: "\f0c3"; } .fa-cut:before, .fa-scissors:before { content: "\f0c4"; } .fa-copy:before, .fa-files-o:before { content: "\f0c5"; } .fa-paperclip:before { content: "\f0c6"; } .fa-save:before, .fa-floppy-o:before { content: "\f0c7"; } .fa-square:before { content: "\f0c8"; } .fa-navicon:before, .fa-reorder:before, .fa-bars:before { content: "\f0c9"; } .fa-list-ul:before { content: "\f0ca"; } .fa-list-ol:before { content: "\f0cb"; } .fa-strikethrough:before { content: "\f0cc"; } .fa-underline:before { content: "\f0cd"; } .fa-table:before { content: "\f0ce"; } .fa-magic:before { content: "\f0d0"; } .fa-truck:before { content: "\f0d1"; } .fa-pinterest:before { content: "\f0d2"; } .fa-pinterest-square:before { content: "\f0d3"; } .fa-google-plus-square:before { content: "\f0d4"; } .fa-google-plus:before { content: "\f0d5"; } .fa-money:before { content: "\f0d6"; } .fa-caret-down:before { content: "\f0d7"; } .fa-caret-up:before { content: "\f0d8"; } .fa-caret-left:before { content: "\f0d9"; } .fa-caret-right:before { content: "\f0da"; } .fa-columns:before { content: "\f0db"; } .fa-unsorted:before, .fa-sort:before { content: "\f0dc"; } .fa-sort-down:before, .fa-sort-desc:before { content: "\f0dd"; } .fa-sort-up:before, .fa-sort-asc:before { content: "\f0de"; } .fa-envelope:before { content: "\f0e0"; } .fa-linkedin:before { content: "\f0e1"; } .fa-rotate-left:before, .fa-undo:before { content: "\f0e2"; } .fa-legal:before, .fa-gavel:before { content: "\f0e3"; } .fa-dashboard:before, .fa-tachometer:before { content: "\f0e4"; } .fa-comment-o:before { content: "\f0e5"; } .fa-comments-o:before { content: "\f0e6"; } .fa-flash:before, .fa-bolt:before { content: "\f0e7"; } .fa-sitemap:before { content: "\f0e8"; } .fa-umbrella:before { content: "\f0e9"; } .fa-paste:before, .fa-clipboard:before { content: "\f0ea"; } .fa-lightbulb-o:before { content: "\f0eb"; } .fa-exchange:before { content: "\f0ec"; } .fa-cloud-download:before { content: "\f0ed"; } .fa-cloud-upload:before { content: "\f0ee"; } .fa-user-md:before { content: "\f0f0"; } .fa-stethoscope:before { content: "\f0f1"; } .fa-suitcase:before { content: "\f0f2"; } .fa-bell-o:before { content: "\f0a2"; } .fa-coffee:before { content: "\f0f4"; } .fa-cutlery:before { content: "\f0f5"; } .fa-file-text-o:before { content: "\f0f6"; } .fa-building-o:before { content: "\f0f7"; } .fa-hospital-o:before { content: "\f0f8"; } .fa-ambulance:before { content: "\f0f9"; } .fa-medkit:before { content: "\f0fa"; } .fa-fighter-jet:before { content: "\f0fb"; } .fa-beer:before { content: "\f0fc"; } .fa-h-square:before { content: "\f0fd"; } .fa-plus-square:before { content: "\f0fe"; } .fa-angle-double-left:before { content: "\f100"; } .fa-angle-double-right:before { content: "\f101"; } .fa-angle-double-up:before { content: "\f102"; } .fa-angle-double-down:before { content: "\f103"; } .fa-angle-left:before { content: "\f104"; } .fa-angle-right:before { content: "\f105"; } .fa-angle-up:before { content: "\f106"; } .fa-angle-down:before { content: "\f107"; } .fa-desktop:before { content: "\f108"; } .fa-laptop:before { content: "\f109"; } .fa-tablet:before { content: "\f10a"; } .fa-mobile-phone:before, .fa-mobile:before { content: "\f10b"; } .fa-circle-o:before { content: "\f10c"; } .fa-quote-left:before { content: "\f10d"; } .fa-quote-right:before { content: "\f10e"; } .fa-spinner:before { content: "\f110"; } .fa-circle:before { content: "\f111"; } .fa-mail-reply:before, .fa-reply:before { content: "\f112"; } .fa-github-alt:before { content: "\f113"; } .fa-folder-o:before { content: "\f114"; } .fa-folder-open-o:before { content: "\f115"; } .fa-smile-o:before { content: "\f118"; } .fa-frown-o:before { content: "\f119"; } .fa-meh-o:before { content: "\f11a"; } .fa-gamepad:before { content: "\f11b"; } .fa-keyboard-o:before { content: "\f11c"; } .fa-flag-o:before { content: "\f11d"; } .fa-flag-checkered:before { content: "\f11e"; } .fa-terminal:before { content: "\f120"; } .fa-code:before { content: "\f121"; } .fa-mail-reply-all:before, .fa-reply-all:before { content: "\f122"; } .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { content: "\f123"; } .fa-location-arrow:before { content: "\f124"; } .fa-crop:before { content: "\f125"; } .fa-code-fork:before { content: "\f126"; } .fa-unlink:before, .fa-chain-broken:before { content: "\f127"; } .fa-question:before { content: "\f128"; } .fa-info:before { content: "\f129"; } .fa-exclamation:before { content: "\f12a"; } .fa-superscript:before { content: "\f12b"; } .fa-subscript:before { content: "\f12c"; } .fa-eraser:before { content: "\f12d"; } .fa-puzzle-piece:before { content: "\f12e"; } .fa-microphone:before { content: "\f130"; } .fa-microphone-slash:before { content: "\f131"; } .fa-shield:before { content: "\f132"; } .fa-calendar-o:before { content: "\f133"; } .fa-fire-extinguisher:before { content: "\f134"; } .fa-rocket:before { content: "\f135"; } .fa-maxcdn:before { content: "\f136"; } .fa-chevron-circle-left:before { content: "\f137"; } .fa-chevron-circle-right:before { content: "\f138"; } .fa-chevron-circle-up:before { content: "\f139"; } .fa-chevron-circle-down:before { content: "\f13a"; } .fa-html5:before { content: "\f13b"; } .fa-css3:before { content: "\f13c"; } .fa-anchor:before { content: "\f13d"; } .fa-unlock-alt:before { content: "\f13e"; } .fa-bullseye:before { content: "\f140"; } .fa-ellipsis-h:before { content: "\f141"; } .fa-ellipsis-v:before { content: "\f142"; } .fa-rss-square:before { content: "\f143"; } .fa-play-circle:before { content: "\f144"; } .fa-ticket:before { content: "\f145"; } .fa-minus-square:before { content: "\f146"; } .fa-minus-square-o:before { content: "\f147"; } .fa-level-up:before { content: "\f148"; } .fa-level-down:before { content: "\f149"; } .fa-check-square:before { content: "\f14a"; } .fa-pencil-square:before { content: "\f14b"; } .fa-external-link-square:before { content: "\f14c"; } .fa-share-square:before { content: "\f14d"; } .fa-compass:before { content: "\f14e"; } .fa-toggle-down:before, .fa-caret-square-o-down:before { content: "\f150"; } .fa-toggle-up:before, .fa-caret-square-o-up:before { content: "\f151"; } .fa-toggle-right:before, .fa-caret-square-o-right:before { content: "\f152"; } .fa-euro:before, .fa-eur:before { content: "\f153"; } .fa-gbp:before { content: "\f154"; } .fa-dollar:before, .fa-usd:before { content: "\f155"; } .fa-rupee:before, .fa-inr:before { content: "\f156"; } .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { content: "\f157"; } .fa-ruble:before, .fa-rouble:before, .fa-rub:before { content: "\f158"; } .fa-won:before, .fa-krw:before { content: "\f159"; } .fa-bitcoin:before, .fa-btc:before { content: "\f15a"; } .fa-file:before { content: "\f15b"; } .fa-file-text:before { content: "\f15c"; } .fa-sort-alpha-asc:before { content: "\f15d"; } .fa-sort-alpha-desc:before { content: "\f15e"; } .fa-sort-amount-asc:before { content: "\f160"; } .fa-sort-amount-desc:before { content: "\f161"; } .fa-sort-numeric-asc:before { content: "\f162"; } .fa-sort-numeric-desc:before { content: "\f163"; } .fa-thumbs-up:before { content: "\f164"; } .fa-thumbs-down:before { content: "\f165"; } .fa-youtube-square:before { content: "\f166"; } .fa-youtube:before { content: "\f167"; } .fa-xing:before { content: "\f168"; } .fa-xing-square:before { content: "\f169"; } .fa-youtube-play:before { content: "\f16a"; } .fa-dropbox:before { content: "\f16b"; } .fa-stack-overflow:before { content: "\f16c"; } .fa-instagram:before { content: "\f16d"; } .fa-flickr:before { content: "\f16e"; } .fa-adn:before { content: "\f170"; } .fa-bitbucket:before { content: "\f171"; } .fa-bitbucket-square:before { content: "\f172"; } .fa-tumblr:before { content: "\f173"; } .fa-tumblr-square:before { content: "\f174"; } .fa-long-arrow-down:before { content: "\f175"; } .fa-long-arrow-up:before { content: "\f176"; } .fa-long-arrow-left:before { content: "\f177"; } .fa-long-arrow-right:before { content: "\f178"; } .fa-apple:before { content: "\f179"; } .fa-windows:before { content: "\f17a"; } .fa-android:before { content: "\f17b"; } .fa-linux:before { content: "\f17c"; } .fa-dribbble:before { content: "\f17d"; } .fa-skype:before { content: "\f17e"; } .fa-foursquare:before { content: "\f180"; } .fa-trello:before { content: "\f181"; } .fa-female:before { content: "\f182"; } .fa-male:before { content: "\f183"; } .fa-gittip:before { content: "\f184"; } .fa-sun-o:before { content: "\f185"; } .fa-moon-o:before { content: "\f186"; } .fa-archive:before { content: "\f187"; } .fa-bug:before { content: "\f188"; } .fa-vk:before { content: "\f189"; } .fa-weibo:before { content: "\f18a"; } .fa-renren:before { content: "\f18b"; } .fa-pagelines:before { content: "\f18c"; } .fa-stack-exchange:before { content: "\f18d"; } .fa-arrow-circle-o-right:before { content: "\f18e"; } .fa-arrow-circle-o-left:before { content: "\f190"; } .fa-toggle-left:before, .fa-caret-square-o-left:before { content: "\f191"; } .fa-dot-circle-o:before { content: "\f192"; } .fa-wheelchair:before { content: "\f193"; } .fa-vimeo-square:before { content: "\f194"; } .fa-turkish-lira:before, .fa-try:before { content: "\f195"; } .fa-plus-square-o:before { content: "\f196"; } .fa-space-shuttle:before { content: "\f197"; } .fa-slack:before { content: "\f198"; } .fa-envelope-square:before { content: "\f199"; } .fa-wordpress:before { content: "\f19a"; } .fa-openid:before { content: "\f19b"; } .fa-institution:before, .fa-bank:before, .fa-university:before { content: "\f19c"; } .fa-mortar-board:before, .fa-graduation-cap:before { content: "\f19d"; } .fa-yahoo:before { content: "\f19e"; } .fa-google:before { content: "\f1a0"; } .fa-reddit:before { content: "\f1a1"; } .fa-reddit-square:before { content: "\f1a2"; } .fa-stumbleupon-circle:before { content: "\f1a3"; } .fa-stumbleupon:before { content: "\f1a4"; } .fa-delicious:before { content: "\f1a5"; } .fa-digg:before { content: "\f1a6"; } .fa-pied-piper:before { content: "\f1a7"; } .fa-pied-piper-alt:before { content: "\f1a8"; } .fa-drupal:before { content: "\f1a9"; } .fa-joomla:before { content: "\f1aa"; } .fa-language:before { content: "\f1ab"; } .fa-fax:before { content: "\f1ac"; } .fa-building:before { content: "\f1ad"; } .fa-child:before { content: "\f1ae"; } .fa-paw:before { content: "\f1b0"; } .fa-spoon:before { content: "\f1b1"; } .fa-cube:before { content: "\f1b2"; } .fa-cubes:before { content: "\f1b3"; } .fa-behance:before { content: "\f1b4"; } .fa-behance-square:before { content: "\f1b5"; } .fa-steam:before { content: "\f1b6"; } .fa-steam-square:before { content: "\f1b7"; } .fa-recycle:before { content: "\f1b8"; } .fa-automobile:before, .fa-car:before { content: "\f1b9"; } .fa-cab:before, .fa-taxi:before { content: "\f1ba"; } .fa-tree:before { content: "\f1bb"; } .fa-spotify:before { content: "\f1bc"; } .fa-deviantart:before { content: "\f1bd"; } .fa-soundcloud:before { content: "\f1be"; } .fa-database:before { content: "\f1c0"; } .fa-file-pdf-o:before { content: "\f1c1"; } .fa-file-word-o:before { content: "\f1c2"; } .fa-file-excel-o:before { content: "\f1c3"; } .fa-file-powerpoint-o:before { content: "\f1c4"; } .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { content: "\f1c5"; } .fa-file-zip-o:before, .fa-file-archive-o:before { content: "\f1c6"; } .fa-file-sound-o:before, .fa-file-audio-o:before { content: "\f1c7"; } .fa-file-movie-o:before, .fa-file-video-o:before { content: "\f1c8"; } .fa-file-code-o:before { content: "\f1c9"; } .fa-vine:before { content: "\f1ca"; } .fa-codepen:before { content: "\f1cb"; } .fa-jsfiddle:before { content: "\f1cc"; } .fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { content: "\f1cd"; } .fa-circle-o-notch:before { content: "\f1ce"; } .fa-ra:before, .fa-rebel:before { content: "\f1d0"; } .fa-ge:before, .fa-empire:before { content: "\f1d1"; } .fa-git-square:before { content: "\f1d2"; } .fa-git:before { content: "\f1d3"; } .fa-hacker-news:before { content: "\f1d4"; } .fa-tencent-weibo:before { content: "\f1d5"; } .fa-qq:before { content: "\f1d6"; } .fa-wechat:before, .fa-weixin:before { content: "\f1d7"; } .fa-send:before, .fa-paper-plane:before { content: "\f1d8"; } .fa-send-o:before, .fa-paper-plane-o:before { content: "\f1d9"; } .fa-history:before { content: "\f1da"; } .fa-circle-thin:before { content: "\f1db"; } .fa-header:before { content: "\f1dc"; } .fa-paragraph:before { content: "\f1dd"; } .fa-sliders:before { content: "\f1de"; } .fa-share-alt:before { content: "\f1e0"; } .fa-share-alt-square:before { content: "\f1e1"; } .fa-bomb:before { content: "\f1e2"; } .fa-soccer-ball-o:before, .fa-futbol-o:before { content: "\f1e3"; } .fa-tty:before { content: "\f1e4"; } .fa-binoculars:before { content: "\f1e5"; } .fa-plug:before { content: "\f1e6"; } .fa-slideshare:before { content: "\f1e7"; } .fa-twitch:before { content: "\f1e8"; } .fa-yelp:before { content: "\f1e9"; } .fa-newspaper-o:before { content: "\f1ea"; } .fa-wifi:before { content: "\f1eb"; } .fa-calculator:before { content: "\f1ec"; } .fa-paypal:before { content: "\f1ed"; } .fa-google-wallet:before { content: "\f1ee"; } .fa-cc-visa:before { content: "\f1f0"; } .fa-cc-mastercard:before { content: "\f1f1"; } .fa-cc-discover:before { content: "\f1f2"; } .fa-cc-amex:before { content: "\f1f3"; } .fa-cc-paypal:before { content: "\f1f4"; } .fa-cc-stripe:before { content: "\f1f5"; } .fa-bell-slash:before { content: "\f1f6"; } .fa-bell-slash-o:before { content: "\f1f7"; } .fa-trash:before { content: "\f1f8"; } .fa-copyright:before { content: "\f1f9"; } .fa-at:before { content: "\f1fa"; } .fa-eyedropper:before { content: "\f1fb"; } .fa-paint-brush:before { content: "\f1fc"; } .fa-birthday-cake:before { content: "\f1fd"; } .fa-area-chart:before { content: "\f1fe"; } .fa-pie-chart:before { content: "\f200"; } .fa-line-chart:before { content: "\f201"; } .fa-lastfm:before { content: "\f202"; } .fa-lastfm-square:before { content: "\f203"; } .fa-toggle-off:before { content: "\f204"; } .fa-toggle-on:before { content: "\f205"; } .fa-bicycle:before { content: "\f206"; } .fa-bus:before { content: "\f207"; } .fa-ioxhost:before { content: "\f208"; } .fa-angellist:before { content: "\f209"; } .fa-cc:before { content: "\f20a"; } .fa-shekel:before, .fa-sheqel:before, .fa-ils:before { content: "\f20b"; } .fa-meanpath:before { content: "\f20c"; } ================================================ FILE: public/libs/jquery-2.1.0.js ================================================ /*! * jQuery JavaScript Library v2.1.0 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2014-01-23T21:10Z */ (function (global, factory) { if (typeof module === "object" && typeof module.exports === "object") { // For CommonJS and CommonJS-like environments where a proper window is present, // execute the factory and get jQuery // For environments that do not inherently posses a window with a document // (such as Node.js), expose a jQuery-making factory as module.exports // This accentuates the need for the creation of a real window // e.g. var jQuery = require("jquery")(window); // See ticket #14549 for more info module.exports = global.document ? factory(global, true) : function (w) { if (!w.document) { throw new Error("jQuery requires a window with a document"); } return factory(w); }; } else { factory(global); } // Pass this if window is not defined yet }(typeof window !== "undefined" ? window : this, function (window, noGlobal) { // Can't do this because several apps including ASP.NET trace // the stack via arguments.caller.callee and Firefox dies if // you try to trace through "use strict" call chains. (#13335) // Support: Firefox 18+ // var arr = []; var slice = arr.slice; var concat = arr.concat; var push = arr.push; var indexOf = arr.indexOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var trim = "".trim; var support = {}; var // Use the correct document accordingly with window argument (sandbox) document = window.document, version = "2.1.0", // Define a local copy of jQuery jQuery = function (selector, context) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init(selector, context); }, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, // Used by jQuery.camelCase as callback to replace() fcamelCase = function (all, letter) { return letter.toUpperCase(); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // Start with an empty selector selector: "", // The default length of a jQuery object is 0 length: 0, toArray: function () { return slice.call(this); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function (num) { return num != null ? // Return a 'clean' array ( num < 0 ? this[ num + this.length ] : this[ num ] ) : // Return just the object slice.call(this); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function (elems) { // Build a new jQuery matched element set var ret = jQuery.merge(this.constructor(), elems); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function (callback, args) { return jQuery.each(this, callback, args); }, map: function (callback) { return this.pushStack(jQuery.map(this, function (elem, i) { return callback.call(elem, i, elem); })); }, slice: function () { return this.pushStack(slice.apply(this, arguments)); }, first: function () { return this.eq(0); }, last: function () { return this.eq(-1); }, eq: function (i) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack(j >= 0 && j < len ? [ this[j] ] : []); }, end: function () { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: arr.sort, splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function () { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if (typeof target === "boolean") { deep = target; // skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if (typeof target !== "object" && !jQuery.isFunction(target)) { target = {}; } // extend jQuery itself if only one argument is passed if (i === length) { target = this; i--; } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[ i ]) != null) { // Extend the base object for (name in options) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) )) { if (copyIsArray) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend(deep, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend({ // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace(/\D/g, ""), // Assume jQuery is ready without the ready module isReady: true, error: function (msg) { throw new Error(msg); }, noop: function () { }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function (obj) { return jQuery.type(obj) === "function"; }, isArray: Array.isArray, isWindow: function (obj) { return obj != null && obj === obj.window; }, isNumeric: function (obj) { // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN return obj - parseFloat(obj) >= 0; }, isPlainObject: function (obj) { // Not plain objects: // - Any object or value whose internal [[Class]] property is not "[object Object]" // - DOM nodes // - window if (jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow(obj)) { return false; } // Support: Firefox <20 // The try/catch suppresses exceptions thrown when attempting to access // the "constructor" property of certain host objects, ie. |window.location| // https://bugzilla.mozilla.org/show_bug.cgi?id=814622 try { if (obj.constructor && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { return false; } } catch (e) { return false; } // If the function hasn't returned already, we're confident that // |obj| is a plain object, created by {} or constructed with new Object return true; }, isEmptyObject: function (obj) { var name; for (name in obj) { return false; } return true; }, type: function (obj) { if (obj == null) { return obj + ""; } // Support: Android < 4.0, iOS < 6 (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call(obj) ] || "object" : typeof obj; }, // Evaluates a script in a global context globalEval: function (code) { var script, indirect = eval; code = jQuery.trim(code); if (code) { // If the code includes a valid, prologue position // strict mode pragma, execute code by injecting a // script tag into the document. if (code.indexOf("use strict") === 1) { script = document.createElement("script"); script.text = code; document.head.appendChild(script).parentNode.removeChild(script); } else { // Otherwise, avoid the DOM node creation, insertion // and removal by using an indirect global eval indirect(code); } } }, // Convert dashed to camelCase; used by the css and data modules // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function (string) { return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase); }, nodeName: function (elem, name) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, // args is for internal usage only each: function (obj, callback, args) { var value, i = 0, length = obj.length, isArray = isArraylike(obj); if (args) { if (isArray) { for (; i < length; i++) { value = callback.apply(obj[ i ], args); if (value === false) { break; } } } else { for (i in obj) { value = callback.apply(obj[ i ], args); if (value === false) { break; } } } // A special, fast, case for the most common use of each } else { if (isArray) { for (; i < length; i++) { value = callback.call(obj[ i ], i, obj[ i ]); if (value === false) { break; } } } else { for (i in obj) { value = callback.call(obj[ i ], i, obj[ i ]); if (value === false) { break; } } } } return obj; }, trim: function (text) { return text == null ? "" : trim.call(text); }, // results is for internal usage only makeArray: function (arr, results) { var ret = results || []; if (arr != null) { if (isArraylike(Object(arr))) { jQuery.merge(ret, typeof arr === "string" ? [ arr ] : arr ); } else { push.call(ret, arr); } } return ret; }, inArray: function (elem, arr, i) { return arr == null ? -1 : indexOf.call(arr, elem, i); }, merge: function (first, second) { var len = +second.length, j = 0, i = first.length; for (; j < len; j++) { first[ i++ ] = second[ j ]; } first.length = i; return first; }, grep: function (elems, callback, invert) { var callbackInverse, matches = [], i = 0, length = elems.length, callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for (; i < length; i++) { callbackInverse = !callback(elems[ i ], i); if (callbackInverse !== callbackExpect) { matches.push(elems[ i ]); } } return matches; }, // arg is for internal usage only map: function (elems, callback, arg) { var value, i = 0, length = elems.length, isArray = isArraylike(elems), ret = []; // Go through the array, translating each of the items to their new values if (isArray) { for (; i < length; i++) { value = callback(elems[ i ], i, arg); if (value != null) { ret.push(value); } } // Go through every key on the object, } else { for (i in elems) { value = callback(elems[ i ], i, arg); if (value != null) { ret.push(value); } } } // Flatten any nested arrays return concat.apply([], ret); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function (fn, context) { var tmp, args, proxy; if (typeof context === "string") { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if (!jQuery.isFunction(fn)) { return undefined; } // Simulated bind args = slice.call(arguments, 2); proxy = function () { return fn.apply(context || this, args.concat(slice.call(arguments))); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, now: Date.now, // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support }); // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function (i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); function isArraylike(obj) { var length = obj.length, type = jQuery.type(obj); if (type === "function" || jQuery.isWindow(obj)) { return false; } if (obj.nodeType === 1 && length) { return true; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! * Sizzle CSS Selector Engine v1.10.16 * http://sizzlejs.com/ * * Copyright 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2014-01-13 */ (function (window) { var i, support, Expr, getText, isXML, compile, outermostContext, sortInput, hasDuplicate, // Local document vars setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data expando = "sizzle" + -(new Date()), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), sortOrder = function (a, b) { if (a === b) { hasDuplicate = true; } return 0; }, // General-purpose constants strundefined = typeof undefined, MAX_NEGATIVE = 1 << 31, // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], pop = arr.pop, push_native = arr.push, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf if we can't use a native one indexOf = arr.indexOf || function (elem) { var i = 0, len = this.length; for (; i < len; i++) { if (this[i] === elem) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/css3-syntax/#characters characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Loosely modeled on CSS identifier characters // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = characterEncoding.replace("w", "w#"), // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", // Prefer arguments quoted, // then not containing pseudos/brackets, // then attribute selectors/non-parenthetical expressions, // then anything else // These preferences are here to reduce the number of selectors // needing tokenize in the PSEUDO preFilter pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace(3, 8) + ")*)|.*)\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"), rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"), rpseudo = new RegExp(pseudos), ridentifier = new RegExp("^" + identifier + "$"), matchExpr = { "ID": new RegExp("^#(" + characterEncoding + ")"), "CLASS": new RegExp("^\\.(" + characterEncoding + ")"), "TAG": new RegExp("^(" + characterEncoding.replace("w", "w*") + ")"), "ATTR": new RegExp("^" + attributes), "PSEUDO": new RegExp("^" + pseudos), "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i"), "bool": new RegExp("^(?:" + booleans + ")$", "i"), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i") }, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"), funescape = function (_, escaped, escapedWhitespace) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint // Support: Firefox // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00); }; // Optimize for push.apply( _, NodeList ) try { push.apply( (arr = slice.call(preferredDoc.childNodes)), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply arr[ preferredDoc.childNodes.length ].nodeType; } catch (e) { push = { apply: arr.length ? // Leverage slice if possible function (target, els) { push_native.apply(target, slice.call(els)); } : // Support: IE<9 // Otherwise append directly function (target, els) { var j = target.length, i = 0; // Can't trust NodeList.length while ((target[j++] = els[i++])) { } target.length = j - 1; } }; } function Sizzle(selector, context, results, seed) { var match, elem, m, nodeType, // QSA vars i, groups, old, nid, newContext, newSelector; if (( context ? context.ownerDocument || context : preferredDoc ) !== document) { setDocument(context); } context = context || document; results = results || []; if (!selector || typeof selector !== "string") { return results; } if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) { return []; } if (documentIsHTML && !seed) { // Shortcuts if ((match = rquickExpr.exec(selector))) { // Speed-up: Sizzle("#ID") if ((m = match[1])) { if (nodeType === 9) { elem = context.getElementById(m); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document (jQuery #6963) if (elem && elem.parentNode) { // Handle the case where IE, Opera, and Webkit return items // by name instead of ID if (elem.id === m) { results.push(elem); return results; } } else { return results; } } else { // Context is not a document if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) && contains(context, elem) && elem.id === m) { results.push(elem); return results; } } // Speed-up: Sizzle("TAG") } else if (match[2]) { push.apply(results, context.getElementsByTagName(selector)); return results; // Speed-up: Sizzle(".CLASS") } else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) { push.apply(results, context.getElementsByClassName(m)); return results; } } // QSA path if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) { nid = old = expando; newContext = context; newSelector = nodeType === 9 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") { groups = tokenize(selector); if ((old = context.getAttribute("id"))) { nid = old.replace(rescape, "\\$&"); } else { context.setAttribute("id", nid); } nid = "[id='" + nid + "'] "; i = groups.length; while (i--) { groups[i] = nid + toSelector(groups[i]); } newContext = rsibling.test(selector) && testContext(context.parentNode) || context; newSelector = groups.join(","); } if (newSelector) { try { push.apply(results, newContext.querySelectorAll(newSelector) ); return results; } catch (qsaError) { } finally { if (!old) { context.removeAttribute("id"); } } } } } // All others return select(selector.replace(rtrim, "$1"), context, results, seed); } /** * Create key-value caches of limited size * @returns {Function(string, Object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache(key, value) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if (keys.push(key + " ") > Expr.cacheLength) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key + " " ] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction(fn) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created div and expects a boolean result */ function assert(fn) { var div = document.createElement("div"); try { return !!fn(div); } catch (e) { return false; } finally { // Remove from its parent by default if (div.parentNode) { div.parentNode.removeChild(div); } // release memory in IE div = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ function addHandle(attrs, handler) { var arr = attrs.split("|"), i = attrs.length; while (i--) { Expr.attrHandle[ arr[i] ] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck(a, b) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); // Use IE sourceIndex if available on both nodes if (diff) { return diff; } // Check if b follows a if (cur) { while ((cur = cur.nextSibling)) { if (cur === b) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo(type) { return function (elem) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo(type) { return function (elem) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo(fn) { return markFunction(function (argument) { argument = +argument; return markFunction(function (seed, matches) { var j, matchIndexes = fn([], seed.length, argument), i = matchIndexes.length; // Match elements found at the specified indexes while (i--) { if (seed[ (j = matchIndexes[i]) ]) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext(context) { return context && typeof context.getElementsByTagName !== strundefined && context; } // Expose support vars for convenience support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function (elem) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function (node) { var hasCompare, doc = node ? node.ownerDocument || node : preferredDoc, parent = doc.defaultView; // If no document and documentElement is available, return if (doc === document || doc.nodeType !== 9 || !doc.documentElement) { return document; } // Set our document document = doc; docElem = doc.documentElement; // Support tests documentIsHTML = !isXML(doc); // Support: IE>8 // If iframe document is assigned to "document" variable and if iframe has been reloaded, // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 // IE6-8 do not support the defaultView property so parent will be undefined if (parent && parent !== parent.top) { // IE11 does not have attachEvent, so all must suffer if (parent.addEventListener) { parent.addEventListener("unload", function () { setDocument(); }, false); } else if (parent.attachEvent) { parent.attachEvent("onunload", function () { setDocument(); }); } } /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) support.attributes = assert(function (div) { div.className = "i"; return !div.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function (div) { div.appendChild(doc.createComment("")); return !div.getElementsByTagName("*").length; }); // Check if getElementsByClassName can be trusted support.getElementsByClassName = rnative.test(doc.getElementsByClassName) && assert(function (div) { div.innerHTML = "
"; // Support: Safari<4 // Catch class over-caching div.firstChild.className = "i"; // Support: Opera<10 // Catch gEBCN failure to find non-leading classes return div.getElementsByClassName("i").length === 2; }); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programatically-set names, // so use a roundabout getElementsByName test support.getById = assert(function (div) { docElem.appendChild(div).id = expando; return !doc.getElementsByName || !doc.getElementsByName(expando).length; }); // ID find and filter if (support.getById) { Expr.find["ID"] = function (id, context) { if (typeof context.getElementById !== strundefined && documentIsHTML) { var m = context.getElementById(id); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [m] : []; } }; Expr.filter["ID"] = function (id) { var attrId = id.replace(runescape, funescape); return function (elem) { return elem.getAttribute("id") === attrId; }; }; } else { // Support: IE6/7 // getElementById is not reliable as a find shortcut delete Expr.find["ID"]; Expr.filter["ID"] = function (id) { var attrId = id.replace(runescape, funescape); return function (elem) { var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; } // Tag Expr.find["TAG"] = support.getElementsByTagName ? function (tag, context) { if (typeof context.getElementsByTagName !== strundefined) { return context.getElementsByTagName(tag); } } : function (tag, context) { var elem, tmp = [], i = 0, results = context.getElementsByTagName(tag); // Filter out possible comments if (tag === "*") { while ((elem = results[i++])) { if (elem.nodeType === 1) { tmp.push(elem); } } return tmp; } return results; }; // Class Expr.find["CLASS"] = support.getElementsByClassName && function (className, context) { if (typeof context.getElementsByClassName !== strundefined && documentIsHTML) { return context.getElementsByClassName(className); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See http://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ((support.qsa = rnative.test(doc.querySelectorAll))) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function (div) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 div.innerHTML = ""; // Support: IE8, Opera 10-12 // Nothing should be selected when empty strings follow ^= or $= or *= if (div.querySelectorAll("[t^='']").length) { rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")"); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if (!div.querySelectorAll("[selected]").length) { rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")"); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if (!div.querySelectorAll(":checked").length) { rbuggyQSA.push(":checked"); } }); assert(function (div) { // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute("type", "hidden"); div.appendChild(input).setAttribute("name", "D"); // Support: IE8 // Enforce case-sensitivity of name attribute if (div.querySelectorAll("[name=d]").length) { rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?="); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if (!div.querySelectorAll(":enabled").length) { rbuggyQSA.push(":enabled", ":disabled"); } // Opera 10-11 does not throw on post-comma invalid pseudos div.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ((support.matchesSelector = rnative.test((matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector)))) { assert(function (div) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call(div, "div"); // This should fail with an exception // Gecko does not error, returns false instead matches.call(div, "[s!='']:x"); rbuggyMatches.push("!=", pseudos); }); } rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|")); rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|")); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test(docElem.compareDocumentPosition); // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself contains = hasCompare || rnative.test(docElem.contains) ? function (a, b) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains(bup) : a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 )); } : function (a, b) { if (b) { while ((b = b.parentNode)) { if (b === a) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting sortOrder = hasCompare ? function (a, b) { // Flag for duplicate removal if (a === b) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if (compare) { return compare; } // Calculate position if both inputs belong to the same document compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? a.compareDocumentPosition(b) : // Otherwise we know they are disconnected 1; // Disconnected nodes if (compare & 1 || (!support.sortDetached && b.compareDocumentPosition(a) === compare)) { // Choose the first element that is related to our preferred document if (a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) { return -1; } if (b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) { return 1; } // Maintain original order return sortInput ? ( indexOf.call(sortInput, a) - indexOf.call(sortInput, b) ) : 0; } return compare & 4 ? -1 : 1; } : function (a, b) { // Exit early if the nodes are identical if (a === b) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Parentless nodes are either documents or disconnected if (!aup || !bup) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf.call(sortInput, a) - indexOf.call(sortInput, b) ) : 0; // If the nodes are siblings, we can do a quick check } else if (aup === bup) { return siblingCheck(a, b); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ((cur = cur.parentNode)) { ap.unshift(cur); } cur = b; while ((cur = cur.parentNode)) { bp.unshift(cur); } // Walk down the tree looking for a discrepancy while (ap[i] === bp[i]) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck(ap[i], bp[i]) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return doc; }; Sizzle.matches = function (expr, elements) { return Sizzle(expr, null, null, elements); }; Sizzle.matchesSelector = function (elem, expr) { // Set document vars if needed if (( elem.ownerDocument || elem ) !== document) { setDocument(elem); } // Make sure that attribute selectors are quoted expr = expr.replace(rattributeQuotes, "='$1']"); if (support.matchesSelector && documentIsHTML && ( !rbuggyMatches || !rbuggyMatches.test(expr) ) && ( !rbuggyQSA || !rbuggyQSA.test(expr) )) { try { var ret = matches.call(elem, expr); // IE 9's matchesSelector returns false on disconnected nodes if (ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11) { return ret; } } catch (e) { } } return Sizzle(expr, document, null, [elem]).length > 0; }; Sizzle.contains = function (context, elem) { // Set document vars if needed if (( context.ownerDocument || context ) !== document) { setDocument(context); } return contains(context, elem); }; Sizzle.attr = function (elem, name) { // Set document vars if needed if (( elem.ownerDocument || elem ) !== document) { setDocument(elem); } var fn = Expr.attrHandle[ name.toLowerCase() ], // Don't get fooled by Object.prototype properties (jQuery #13807) val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? fn(elem, name, !documentIsHTML) : undefined; return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute(name) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; }; Sizzle.error = function (msg) { throw new Error("Syntax error, unrecognized expression: " + msg); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function (results) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice(0); results.sort(sortOrder); if (hasDuplicate) { while ((elem = results[i++])) { if (elem === results[ i ]) { j = duplicates.push(i); } } while (j--) { results.splice(duplicates[ j ], 1); } } // Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function (elem) { var node, ret = "", i = 0, nodeType = elem.nodeType; if (!nodeType) { // If no nodeType, this is expected to be an array while ((node = elem[i++])) { // Do not traverse comment nodes ret += getText(node); } } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if (typeof elem.textContent === "string") { return elem.textContent; } else { // Traverse its children for (elem = elem.firstChild; elem; elem = elem.nextSibling) { ret += getText(elem); } } } else if (nodeType === 3 || nodeType === 4) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function (match) { match[1] = match[1].replace(runescape, funescape); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[4] || match[5] || "" ).replace(runescape, funescape); if (match[2] === "~=") { match[3] = " " + match[3] + " "; } return match.slice(0, 4); }, "CHILD": function (match) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if (match[1].slice(0, 3) === "nth") { // nth-* requires argument if (!match[3]) { Sizzle.error(match[0]); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments } else if (match[3]) { Sizzle.error(match[0]); } return match; }, "PSEUDO": function (match) { var excess, unquoted = !match[5] && match[2]; if (matchExpr["CHILD"].test(match[0])) { return null; } // Accept quoted arguments as-is if (match[3] && match[4] !== undefined) { match[2] = match[4]; // Strip excess characters from unquoted arguments } else if (unquoted && rpseudo.test(unquoted) && // Get excess from tokenize (recursively) (excess = tokenize(unquoted, true)) && // advance to the next closing parenthesis (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) { // excess is a negative index match[0] = match[0].slice(0, excess); match[2] = unquoted.slice(0, excess); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice(0, 3); } }, filter: { "TAG": function (nodeNameSelector) { var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); return nodeNameSelector === "*" ? function () { return true; } : function (elem) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function (className) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && classCache(className, function (elem) { return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || ""); }); }, "ATTR": function (name, operator, check) { return function (elem) { var result = Sizzle.attr(elem, name); if (result == null) { return operator === "!="; } if (!operator) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf(check) === 0 : operator === "*=" ? check && result.indexOf(check) > -1 : operator === "$=" ? check && result.slice(-check.length) === check : operator === "~=" ? ( " " + result + " " ).indexOf(check) > -1 : operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : false; }; }, "CHILD": function (type, what, argument, first, last) { var simple = type.slice(0, 3) !== "nth", forward = type.slice(-4) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function (elem) { return !!elem.parentNode; } : function (elem, context, xml) { var cache, outerCache, node, diff, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType; if (parent) { // :(first|last|only)-(child|of-type) if (simple) { while (dir) { node = elem; while ((node = node[ dir ])) { if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if (forward && useCache) { // Seek `elem` from a previously-cached index outerCache = parent[ expando ] || (parent[ expando ] = {}); cache = outerCache[ type ] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = cache[0] === dirruns && cache[2]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ((node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop())) { // When found, cache indexes on `parent` and break if (node.nodeType === 1 && ++diff && node === elem) { outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } // Use previously-cached element index if available } else if (useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns) { diff = cache[1]; // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { // Use the same loop as above to seek `elem` from the start while ((node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop())) { if (( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff) { // Cache the index of each encountered element if (useCache) { (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; } if (node === elem) { break; } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function (pseudo, argument) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error("unsupported pseudo: " + pseudo); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if (fn[ expando ]) { return fn(argument); } // But maintain support for old signatures if (fn.length > 1) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? markFunction(function (seed, matches) { var idx, matched = fn(seed, argument), i = matched.length; while (i--) { idx = indexOf.call(seed, matched[i]); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function (elem) { return fn(elem, 0, args); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction(function (selector) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile(selector.replace(rtrim, "$1")); return matcher[ expando ] ? markFunction(function (seed, matches, context, xml) { var elem, unmatched = matcher(seed, null, xml, []), i = seed.length; // Match elements unmatched by `matcher` while (i--) { if ((elem = unmatched[i])) { seed[i] = !(matches[i] = elem); } } }) : function (elem, context, xml) { input[0] = elem; matcher(input, null, xml, results); return !results.pop(); }; }), "has": markFunction(function (selector) { return function (elem) { return Sizzle(selector, elem).length > 0; }; }), "contains": markFunction(function (text) { return function (elem) { return ( elem.textContent || elem.innerText || getText(elem) ).indexOf(text) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction(function (lang) { // lang value must be a valid identifier if (!ridentifier.test(lang || "")) { Sizzle.error("unsupported lang: " + lang); } lang = lang.replace(runescape, funescape).toLowerCase(); return function (elem) { var elemLang; do { if ((elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf(lang + "-") === 0; } } while ((elem = elem.parentNode) && elem.nodeType === 1); return false; }; }), // Miscellaneous "target": function (elem) { var hash = window.location && window.location.hash; return hash && hash.slice(1) === elem.id; }, "root": function (elem) { return elem === docElem; }, "focus": function (elem) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties "enabled": function (elem) { return elem.disabled === false; }, "disabled": function (elem) { return elem.disabled === true; }, "checked": function (elem) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function (elem) { // Accessing this property makes selected-by-default // options in Safari work properly if (elem.parentNode) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function (elem) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children for (elem = elem.firstChild; elem; elem = elem.nextSibling) { if (elem.nodeType < 6) { return false; } } return true; }, "parent": function (elem) { return !Expr.pseudos["empty"](elem); }, // Element/input types "header": function (elem) { return rheader.test(elem.nodeName); }, "input": function (elem) { return rinputs.test(elem.nodeName); }, "button": function (elem) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function (elem) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection "first": createPositionalPseudo(function () { return [ 0 ]; }), "last": createPositionalPseudo(function (matchIndexes, length) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function (matchIndexes, length, argument) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function (matchIndexes, length) { var i = 0; for (; i < length; i += 2) { matchIndexes.push(i); } return matchIndexes; }), "odd": createPositionalPseudo(function (matchIndexes, length) { var i = 1; for (; i < length; i += 2) { matchIndexes.push(i); } return matchIndexes; }), "lt": createPositionalPseudo(function (matchIndexes, length, argument) { var i = argument < 0 ? argument + length : argument; for (; --i >= 0;) { matchIndexes.push(i); } return matchIndexes; }), "gt": createPositionalPseudo(function (matchIndexes, length, argument) { var i = argument < 0 ? argument + length : argument; for (; ++i < length;) { matchIndexes.push(i); } return matchIndexes; }) } }; Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos for (i in { radio: true, checkbox: true, file: true, password: true, image: true }) { Expr.pseudos[ i ] = createInputPseudo(i); } for (i in { submit: true, reset: true }) { Expr.pseudos[ i ] = createButtonPseudo(i); } // Easy API for creating new setFilters function setFilters() { } setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); function tokenize(selector, parseOnly) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if (cached) { return parseOnly ? 0 : cached.slice(0); } soFar = selector; groups = []; preFilters = Expr.preFilter; while (soFar) { // Comma and first run if (!matched || (match = rcomma.exec(soFar))) { if (match) { // Don't consume trailing commas as valid soFar = soFar.slice(match[0].length) || soFar; } groups.push((tokens = [])); } matched = false; // Combinators if ((match = rcombinators.exec(soFar))) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace(rtrim, " ") }); soFar = soFar.slice(matched.length); } // Filters for (type in Expr.filter) { if ((match = matchExpr[ type ].exec(soFar)) && (!preFilters[ type ] || (match = preFilters[ type ](match)))) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice(matched.length); } } if (!matched) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // Cache the tokens tokenCache(selector, groups).slice(0); } function toSelector(tokens) { var i = 0, len = tokens.length, selector = ""; for (; i < len; i++) { selector += tokens[i].value; } return selector; } function addCombinator(matcher, combinator, base) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function (elem, context, xml) { while ((elem = elem[ dir ])) { if (elem.nodeType === 1 || checkNonElements) { return matcher(elem, context, xml); } } } : // Check against all ancestor/preceding elements function (elem, context, xml) { var oldCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching if (xml) { while ((elem = elem[ dir ])) { if (elem.nodeType === 1 || checkNonElements) { if (matcher(elem, context, xml)) { return true; } } } } else { while ((elem = elem[ dir ])) { if (elem.nodeType === 1 || checkNonElements) { outerCache = elem[ expando ] || (elem[ expando ] = {}); if ((oldCache = outerCache[ dir ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements outerCache[ dir ] = newCache; // A match means we're done; a fail means we have to keep checking if ((newCache[ 2 ] = matcher(elem, context, xml))) { return true; } } } } } }; } function elementMatcher(matchers) { return matchers.length > 1 ? function (elem, context, xml) { var i = matchers.length; while (i--) { if (!matchers[i](elem, context, xml)) { return false; } } return true; } : matchers[0]; } function condense(unmatched, map, filter, context, xml) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for (; i < len; i++) { if ((elem = unmatched[i])) { if (!filter || filter(elem, context, xml)) { newUnmatched.push(elem); if (mapped) { map.push(i); } } } } return newUnmatched; } function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) { if (postFilter && !postFilter[ expando ]) { postFilter = setMatcher(postFilter); } if (postFinder && !postFinder[ expando ]) { postFinder = setMatcher(postFinder, postSelector); } return markFunction(function (seed, results, context, xml) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts(selector || "*", context.nodeType ? [ context ] : context, []), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense(elems, preMap, preFilter, context, xml) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if (matcher) { matcher(matcherIn, matcherOut, context, xml); } // Apply postFilter if (postFilter) { temp = condense(matcherOut, postMap); postFilter(temp, [], context, xml); // Un-match failing elements by moving them back to matcherIn i = temp.length; while (i--) { if ((elem = temp[i])) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if (seed) { if (postFinder || preFilter) { if (postFinder) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while (i--) { if ((elem = matcherOut[i])) { // Restore matcherIn since elem is not yet a final match temp.push((matcherIn[i] = elem)); } } postFinder(null, (matcherOut = []), temp, xml); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while (i--) { if ((elem = matcherOut[i]) && (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice(preexisting, matcherOut.length) : matcherOut ); if (postFinder) { postFinder(null, results, matcherOut, xml); } else { push.apply(results, matcherOut); } } }); } function matcherFromTokens(tokens) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator(function (elem) { return elem === checkContext; }, implicitRelative, true), matchAnyContext = addCombinator(function (elem) { return indexOf.call(checkContext, elem) > -1; }, implicitRelative, true), matchers = [ function (elem, context, xml) { return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext(elem, context, xml) : matchAnyContext(elem, context, xml) ); } ]; for (; i < len; i++) { if ((matcher = Expr.relative[ tokens[i].type ])) { matchers = [ addCombinator(elementMatcher(matchers), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply(null, tokens[i].matches); // Return special upon seeing a positional matcher if (matcher[ expando ]) { // Find the next relative operator (if any) for proper handling j = ++i; for (; j < len; j++) { if (Expr.relative[ tokens[j].type ]) { break; } } return setMatcher( i > 1 && elementMatcher(matchers), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens.slice(0, i - 1).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) ).replace(rtrim, "$1"), matcher, i < j && matcherFromTokens(tokens.slice(i, j)), j < len && matcherFromTokens((tokens = tokens.slice(j))), j < len && toSelector(tokens) ); } matchers.push(matcher); } } return elementMatcher(matchers); } function matcherFromGroupMatchers(elementMatchers, setMatchers) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function (seed, context, xml, results, outermost) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // We must always have either seed elements or outermost context elems = seed || byElement && Expr.find["TAG"]("*", outermost), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if (outermost) { outermostContext = context !== document && context; } // Add elements passing elementMatchers directly to results // Keep `i` a string if there are no elements so `matchedCount` will be "00" below // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id for (; i !== len && (elem = elems[i]) != null; i++) { if (byElement && elem) { j = 0; while ((matcher = elementMatchers[j++])) { if (matcher(elem, context, xml)) { results.push(elem); break; } } if (outermost) { dirruns = dirrunsUnique; } } // Track unmatched elements for set filters if (bySet) { // They will have gone through all possible matchers if ((elem = !matcher && elem)) { matchedCount--; } // Lengthen the array for every element, matched or not if (seed) { unmatched.push(elem); } } } // Apply set filters to unmatched elements matchedCount += i; if (bySet && i !== matchedCount) { j = 0; while ((matcher = setMatchers[j++])) { matcher(unmatched, setMatched, context, xml); } if (seed) { // Reintegrate element matches to eliminate the need for sorting if (matchedCount > 0) { while (i--) { if (!(unmatched[i] || setMatched[i])) { setMatched[i] = pop.call(results); } } } // Discard index placeholder values to get only actual matches setMatched = condense(setMatched); } // Add matches to results push.apply(results, setMatched); // Seedless set matches succeeding multiple successful matchers stipulate sorting if (outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1) { Sizzle.uniqueSort(results); } } // Override manipulation of globals by nested matchers if (outermost) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction(superMatcher) : superMatcher; } compile = Sizzle.compile = function (selector, group /* Internal Use Only */) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if (!cached) { // Generate a function of recursive functions that can be used to check each element if (!group) { group = tokenize(selector); } i = group.length; while (i--) { cached = matcherFromTokens(group[i]); if (cached[ expando ]) { setMatchers.push(cached); } else { elementMatchers.push(cached); } } // Cache the compiled function cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); } return cached; }; function multipleContexts(selector, contexts, results) { var i = 0, len = contexts.length; for (; i < len; i++) { Sizzle(selector, contexts[i], results); } return results; } function select(selector, context, results, seed) { var i, tokens, token, type, find, match = tokenize(selector); if (!seed) { // Try to minimize operations if there is only one group if (match.length === 1) { // Take a shortcut and set the context if the root selector is an ID tokens = match[0] = match[0].slice(0); if (tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ]) { context = ( Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [] )[0]; if (!context) { return results; } selector = selector.slice(tokens.shift().value.length); } // Fetch a seed set for right-to-left matching i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length; while (i--) { token = tokens[i]; // Abort if we hit a combinator if (Expr.relative[ (type = token.type) ]) { break; } if ((find = Expr.find[ type ])) { // Search, expanding context for leading sibling combinators if ((seed = find( token.matches[0].replace(runescape, funescape), rsibling.test(tokens[0].type) && testContext(context.parentNode) || context ))) { // If seed is empty or no tokens remain, we can return early tokens.splice(i, 1); selector = seed.length && toSelector(tokens); if (!selector) { push.apply(results, seed); return results; } break; } } } } } // Compile and execute a filtering function // Provide `match` to avoid retokenization if we modified the selector above compile(selector, match)( seed, context, !documentIsHTML, results, rsibling.test(selector) && testContext(context.parentNode) || context ); return results; } // One-time assignments // Sort stability support.sortStable = expando.split("").sort(sortOrder).join("") === expando; // Support: Chrome<14 // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* support.sortDetached = assert(function (div1) { // Should return 1, but returns 4 (following) return div1.compareDocumentPosition(document.createElement("div")) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if (!assert(function (div) { div.innerHTML = ""; return div.firstChild.getAttribute("href") === "#"; })) { addHandle("type|href|height|width", function (elem, name, isXML) { if (!isXML) { return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2); } }); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") if (!support.attributes || !assert(function (div) { div.innerHTML = ""; div.firstChild.setAttribute("value", ""); return div.firstChild.getAttribute("value") === ""; })) { addHandle("value", function (elem, name, isXML) { if (!isXML && elem.nodeName.toLowerCase() === "input") { return elem.defaultValue; } }); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies if (!assert(function (div) { return div.getAttribute("disabled") == null; })) { addHandle(booleans, function (elem, name, isXML) { var val; if (!isXML) { return elem[ name ] === true ? name.toLowerCase() : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; } }); } return Sizzle; })(window); jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.pseudos; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; var rneedsContext = jQuery.expr.match.needsContext; var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); var risSimple = /^.[^:#\[\.,]*$/; // Implement the identical functionality for filter and not function winnow(elements, qualifier, not) { if (jQuery.isFunction(qualifier)) { return jQuery.grep(elements, function (elem, i) { /* jshint -W018 */ return !!qualifier.call(elem, i, elem) !== not; }); } if (qualifier.nodeType) { return jQuery.grep(elements, function (elem) { return ( elem === qualifier ) !== not; }); } if (typeof qualifier === "string") { if (risSimple.test(qualifier)) { return jQuery.filter(qualifier, elements, not); } qualifier = jQuery.filter(qualifier, elements); } return jQuery.grep(elements, function (elem) { return ( indexOf.call(qualifier, elem) >= 0 ) !== not; }); } jQuery.filter = function (expr, elems, not) { var elem = elems[ 0 ]; if (not) { expr = ":not(" + expr + ")"; } return elems.length === 1 && elem.nodeType === 1 ? jQuery.find.matchesSelector(elem, expr) ? [ elem ] : [] : jQuery.find.matches(expr, jQuery.grep(elems, function (elem) { return elem.nodeType === 1; })); }; jQuery.fn.extend({ find: function (selector) { var i, len = this.length, ret = [], self = this; if (typeof selector !== "string") { return this.pushStack(jQuery(selector).filter(function () { for (i = 0; i < len; i++) { if (jQuery.contains(self[ i ], this)) { return true; } } })); } for (i = 0; i < len; i++) { jQuery.find(selector, self[ i ], ret); } // Needed because $( selector, context ) becomes $( context ).find( selector ) ret = this.pushStack(len > 1 ? jQuery.unique(ret) : ret); ret.selector = this.selector ? this.selector + " " + selector : selector; return ret; }, filter: function (selector) { return this.pushStack(winnow(this, selector || [], false)); }, not: function (selector) { return this.pushStack(winnow(this, selector || [], true)); }, is: function (selector) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test(selector) ? jQuery(selector) : selector || [], false ).length; } }); // Initialize a jQuery object // A central reference to the root jQuery(document) var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, init = jQuery.fn.init = function (selector, context) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if (!selector) { return this; } // Handle HTML strings if (typeof selector === "string") { if (selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec(selector); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (jQuery.isFunction(this[ match ])) { this[ match ](context[ match ]); // ...and otherwise set as attributes } else { this.attr(match, context[ match ]); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById(match[2]); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if (elem && elem.parentNode) { // Inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { return ( context || rootjQuery ).find(selector); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor(context).find(selector); } // HANDLE: $(DOMElement) } else if (selector.nodeType) { this.context = this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if (jQuery.isFunction(selector)) { return typeof rootjQuery.ready !== "undefined" ? rootjQuery.ready(selector) : // Execute immediately if ready is not present selector(jQuery); } if (selector.selector !== undefined) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray(selector, this); }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; // Initialize central reference rootjQuery = jQuery(document); var rparentsprev = /^(?:parents|prev(?:Until|All))/, // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.extend({ dir: function (elem, dir, until) { var matched = [], truncate = until !== undefined; while ((elem = elem[ dir ]) && elem.nodeType !== 9) { if (elem.nodeType === 1) { if (truncate && jQuery(elem).is(until)) { break; } matched.push(elem); } } return matched; }, sibling: function (n, elem) { var matched = []; for (; n; n = n.nextSibling) { if (n.nodeType === 1 && n !== elem) { matched.push(n); } } return matched; } }); jQuery.fn.extend({ has: function (target) { var targets = jQuery(target, this), l = targets.length; return this.filter(function () { var i = 0; for (; i < l; i++) { if (jQuery.contains(this, targets[i])) { return true; } } }); }, closest: function (selectors, context) { var cur, i = 0, l = this.length, matched = [], pos = rneedsContext.test(selectors) || typeof selectors !== "string" ? jQuery(selectors, context || this.context) : 0; for (; i < l; i++) { for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) { // Always skip document fragments if (cur.nodeType < 11 && (pos ? pos.index(cur) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors))) { matched.push(cur); break; } } } return this.pushStack(matched.length > 1 ? jQuery.unique(matched) : matched); }, // Determine the position of an element within // the matched set of elements index: function (elem) { // No argument, return index in parent if (!elem) { return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } // index in selector if (typeof elem === "string") { return indexOf.call(jQuery(elem), this[ 0 ]); } // Locate the position of the desired element return indexOf.call(this, // If it receives a jQuery object, the first element is used elem.jquery ? elem[ 0 ] : elem ); }, add: function (selector, context) { return this.pushStack( jQuery.unique( jQuery.merge(this.get(), jQuery(selector, context)) ) ); }, addBack: function (selector) { return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector) ); } }); function sibling(cur, dir) { while ((cur = cur[dir]) && cur.nodeType !== 1) { } return cur; } jQuery.each({ parent: function (elem) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function (elem) { return jQuery.dir(elem, "parentNode"); }, parentsUntil: function (elem, i, until) { return jQuery.dir(elem, "parentNode", until); }, next: function (elem) { return sibling(elem, "nextSibling"); }, prev: function (elem) { return sibling(elem, "previousSibling"); }, nextAll: function (elem) { return jQuery.dir(elem, "nextSibling"); }, prevAll: function (elem) { return jQuery.dir(elem, "previousSibling"); }, nextUntil: function (elem, i, until) { return jQuery.dir(elem, "nextSibling", until); }, prevUntil: function (elem, i, until) { return jQuery.dir(elem, "previousSibling", until); }, siblings: function (elem) { return jQuery.sibling(( elem.parentNode || {} ).firstChild, elem); }, children: function (elem) { return jQuery.sibling(elem.firstChild); }, contents: function (elem) { return elem.contentDocument || jQuery.merge([], elem.childNodes); } }, function (name, fn) { jQuery.fn[ name ] = function (until, selector) { var matched = jQuery.map(this, fn, until); if (name.slice(-5) !== "Until") { selector = until; } if (selector && typeof selector === "string") { matched = jQuery.filter(selector, matched); } if (this.length > 1) { // Remove duplicates if (!guaranteedUnique[ name ]) { jQuery.unique(matched); } // Reverse order for parents* and prev-derivatives if (rparentsprev.test(name)) { matched.reverse(); } } return this.pushStack(matched); }; }); var rnotwhite = (/\S+/g); // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions(options) { var object = optionsCache[ options ] = {}; jQuery.each(options.match(rnotwhite) || [], function (_, flag) { object[ flag ] = true; }); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function (options) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions(options) ) : jQuery.extend({}, options); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function (data) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for (; list && firingIndex < firingLength; firingIndex++) { if (list[ firingIndex ].apply(data[ 0 ], data[ 1 ]) === false && options.stopOnFalse) { memory = false; // To prevent further calls using add break; } } firing = false; if (list) { if (stack) { if (stack.length) { fire(stack.shift()); } } else if (memory) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function () { if (list) { // First, we save the current length var start = list.length; (function add(args) { jQuery.each(args, function (_, arg) { var type = jQuery.type(arg); if (type === "function") { if (!options.unique || !self.has(arg)) { list.push(arg); } } else if (arg && arg.length && type !== "string") { // Inspect recursively add(arg); } }); })(arguments); // Do we need to add the callbacks to the // current firing batch? if (firing) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if (memory) { firingStart = start; fire(memory); } } return this; }, // Remove a callback from the list remove: function () { if (list) { jQuery.each(arguments, function (_, arg) { var index; while (( index = jQuery.inArray(arg, list, index) ) > -1) { list.splice(index, 1); // Handle firing indexes if (firing) { if (index <= firingLength) { firingLength--; } if (index <= firingIndex) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function (fn) { return fn ? jQuery.inArray(fn, list) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function () { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore disable: function () { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function () { return !list; }, // Lock the list in its current state lock: function () { stack = undefined; if (!memory) { self.disable(); } return this; }, // Is it locked? locked: function () { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function (context, args) { if (list && ( !fired || stack )) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if (firing) { stack.push(args); } else { fire(args); } } return this; }, // Call all the callbacks with the given arguments fire: function () { self.fireWith(this, arguments); return this; }, // To know if the callbacks have already been called at least once fired: function () { return !!fired; } }; return self; }; jQuery.extend({ Deferred: function (func) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function () { return state; }, always: function () { deferred.done(arguments).fail(arguments); return this; }, then: function (/* fnDone, fnFail, fnProgress */) { var fns = arguments; return jQuery.Deferred(function (newDefer) { jQuery.each(tuples, function (i, tuple) { var fn = jQuery.isFunction(fns[ i ]) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function () { var returned = fn && fn.apply(this, arguments); if (returned && jQuery.isFunction(returned.promise)) { returned.promise() .done(newDefer.resolve) .fail(newDefer.reject) .progress(newDefer.notify); } else { newDefer[ tuple[ 0 ] + "With" ](this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function (obj) { return obj != null ? jQuery.extend(obj, promise) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods jQuery.each(tuples, function (i, tuple) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if (stateString) { list.add(function () { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock); } // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function () { deferred[ tuple[0] + "With" ](this === deferred ? promise : this, arguments); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise(deferred); // Call given func if any if (func) { func.call(deferred, deferred); } // All done! return deferred; }, // Deferred helper when: function (subordinate /* , ..., subordinateN */) { var i = 0, resolveValues = slice.call(arguments), length = resolveValues.length, // the count of uncompleted subordinates remaining = length !== 1 || ( subordinate && jQuery.isFunction(subordinate.promise) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function (i, contexts, values) { return function (value) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? slice.call(arguments) : value; if (values === progressValues) { deferred.notifyWith(contexts, values); } else if (!( --remaining )) { deferred.resolveWith(contexts, values); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if (length > 1) { progressValues = new Array(length); progressContexts = new Array(length); resolveContexts = new Array(length); for (; i < length; i++) { if (resolveValues[ i ] && jQuery.isFunction(resolveValues[ i ].promise)) { resolveValues[ i ].promise() .done(updateFunc(i, resolveContexts, resolveValues)) .fail(deferred.reject) .progress(updateFunc(i, progressContexts, progressValues)); } else { --remaining; } } } // if we're not waiting on anything, resolve the master if (!remaining) { deferred.resolveWith(resolveContexts, resolveValues); } return deferred.promise(); } }); // The deferred used on DOM ready var readyList; jQuery.fn.ready = function (fn) { // Add the callback jQuery.ready.promise().done(fn); return this; }; jQuery.extend({ // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function (hold) { if (hold) { jQuery.readyWait++; } else { jQuery.ready(true); } }, // Handle when the DOM is ready ready: function (wait) { // Abort if there are pending holds or we're already ready if (wait === true ? --jQuery.readyWait : jQuery.isReady) { return; } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if (wait !== true && --jQuery.readyWait > 0) { return; } // If there are functions bound, to execute readyList.resolveWith(document, [ jQuery ]); // Trigger any bound ready events if (jQuery.fn.trigger) { jQuery(document).trigger("ready").off("ready"); } } }); /** * The ready event handler and self cleanup method */ function completed() { document.removeEventListener("DOMContentLoaded", completed, false); window.removeEventListener("load", completed, false); jQuery.ready(); } jQuery.ready.promise = function (obj) { if (!readyList) { readyList = jQuery.Deferred(); // Catch cases where $(document).ready() is called after the browser event has already occurred. // we once tried to use readyState "interactive" here, but it caused issues like the one // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 if (document.readyState === "complete") { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout(jQuery.ready); } else { // Use the handy event callback document.addEventListener("DOMContentLoaded", completed, false); // A fallback to window.onload, that will always work window.addEventListener("load", completed, false); } } return readyList.promise(obj); }; // Kick off the DOM ready check even if the user does not jQuery.ready.promise(); // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = jQuery.access = function (elems, fn, key, value, chainable, emptyGet, raw) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if (jQuery.type(key) === "object") { chainable = true; for (i in key) { jQuery.access(elems, fn, i, key[i], true, emptyGet, raw); } // Sets one value } else if (value !== undefined) { chainable = true; if (!jQuery.isFunction(value)) { raw = true; } if (bulk) { // Bulk operations run against the entire set if (raw) { fn.call(elems, value); fn = null; // ...except when executing function values } else { bulk = fn; fn = function (elem, key, value) { return bulk.call(jQuery(elem), value); }; } } if (fn) { for (; i < len; i++) { fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key))); } } } return chainable ? elems : // Gets bulk ? fn.call(elems) : len ? fn(elems[0], key) : emptyGet; }; /** * Determines whether an object can have data */ jQuery.acceptData = function (owner) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any /* jshint -W018 */ return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; function Data() { // Support: Android < 4, // Old WebKit does not have Object.preventExtensions/freeze method, // return new empty object instead with no [[set]] accessor Object.defineProperty(this.cache = {}, 0, { get: function () { return {}; } }); this.expando = jQuery.expando + Math.random(); } Data.uid = 1; Data.accepts = jQuery.acceptData; Data.prototype = { key: function (owner) { // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return the key for a frozen object. if (!Data.accepts(owner)) { return 0; } var descriptor = {}, // Check if the owner object already has a cache key unlock = owner[ this.expando ]; // If not, create one if (!unlock) { unlock = Data.uid++; // Secure it in a non-enumerable, non-writable property try { descriptor[ this.expando ] = { value: unlock }; Object.defineProperties(owner, descriptor); // Support: Android < 4 // Fallback to a less secure definition } catch (e) { descriptor[ this.expando ] = unlock; jQuery.extend(owner, descriptor); } } // Ensure the cache object if (!this.cache[ unlock ]) { this.cache[ unlock ] = {}; } return unlock; }, set: function (owner, data, value) { var prop, // There may be an unlock assigned to this node, // if there is no entry for this "owner", create one inline // and set the unlock as though an owner entry had always existed unlock = this.key(owner), cache = this.cache[ unlock ]; // Handle: [ owner, key, value ] args if (typeof data === "string") { cache[ data ] = value; // Handle: [ owner, { properties } ] args } else { // Fresh assignments by object are shallow copied if (jQuery.isEmptyObject(cache)) { jQuery.extend(this.cache[ unlock ], data); // Otherwise, copy the properties one-by-one to the cache object } else { for (prop in data) { cache[ prop ] = data[ prop ]; } } } return cache; }, get: function (owner, key) { // Either a valid cache is found, or will be created. // New caches will be created and the unlock returned, // allowing direct access to the newly created // empty data object. A valid owner object must be provided. var cache = this.cache[ this.key(owner) ]; return key === undefined ? cache : cache[ key ]; }, access: function (owner, key, value) { var stored; // In cases where either: // // 1. No key was specified // 2. A string key was specified, but no value provided // // Take the "read" path and allow the get method to determine // which value to return, respectively either: // // 1. The entire cache object // 2. The data stored at the key // if (key === undefined || ((key && typeof key === "string") && value === undefined)) { stored = this.get(owner, key); return stored !== undefined ? stored : this.get(owner, jQuery.camelCase(key)); } // [*]When the key is not a string, or both a key and value // are specified, set or extend (existing objects) with either: // // 1. An object of properties // 2. A key and value // this.set(owner, key, value); // Since the "set" path can have two possible entry points // return the expected data based on which path was taken[*] return value !== undefined ? value : key; }, remove: function (owner, key) { var i, name, camel, unlock = this.key(owner), cache = this.cache[ unlock ]; if (key === undefined) { this.cache[ unlock ] = {}; } else { // Support array or space separated string of keys if (jQuery.isArray(key)) { // If "name" is an array of keys... // When data is initially created, via ("key", "val") signature, // keys will be converted to camelCase. // Since there is no way to tell _how_ a key was added, remove // both plain key and camelCase key. #12786 // This will only penalize the array argument path. name = key.concat(key.map(jQuery.camelCase)); } else { camel = jQuery.camelCase(key); // Try the string as a key before any manipulation if (key in cache) { name = [ key, camel ]; } else { // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace name = camel; name = name in cache ? [ name ] : ( name.match(rnotwhite) || [] ); } } i = name.length; while (i--) { delete cache[ name[ i ] ]; } } }, hasData: function (owner) { return !jQuery.isEmptyObject( this.cache[ owner[ this.expando ] ] || {} ); }, discard: function (owner) { if (owner[ this.expando ]) { delete this.cache[ owner[ this.expando ] ]; } } }; var data_priv = new Data(); var data_user = new Data(); /* Implementation Summary 1. Enforce API surface and semantic compatibility with 1.9.x branch 2. Improve the module's maintainability by reducing the storage paths to a single mechanism. 3. Use the same single mechanism to support "private" and "user" data. 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) 5. Avoid exposing implementation details on user objects (eg. expando properties) 6. Provide a clear path for implementation upgrade to WeakMap in 2014 */ var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /([A-Z])/g; function dataAttr(elem, key, data) { var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if (data === undefined && elem.nodeType === 1) { name = "data-" + key.replace(rmultiDash, "-$1").toLowerCase(); data = elem.getAttribute(name); if (typeof data === "string") { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : // Only convert to a number if it doesn't change the string +data + "" === data ? +data : rbrace.test(data) ? jQuery.parseJSON(data) : data; } catch (e) { } // Make sure we set the data so it isn't changed later data_user.set(elem, key, data); } else { data = undefined; } } return data; } jQuery.extend({ hasData: function (elem) { return data_user.hasData(elem) || data_priv.hasData(elem); }, data: function (elem, name, data) { return data_user.access(elem, name, data); }, removeData: function (elem, name) { data_user.remove(elem, name); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to data_priv methods, these can be deprecated. _data: function (elem, name, data) { return data_priv.access(elem, name, data); }, _removeData: function (elem, name) { data_priv.remove(elem, name); } }); jQuery.fn.extend({ data: function (key, value) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values if (key === undefined) { if (this.length) { data = data_user.get(elem); if (elem.nodeType === 1 && !data_priv.get(elem, "hasDataAttrs")) { i = attrs.length; while (i--) { name = attrs[ i ].name; if (name.indexOf("data-") === 0) { name = jQuery.camelCase(name.slice(5)); dataAttr(elem, name, data[ name ]); } } data_priv.set(elem, "hasDataAttrs", true); } } return data; } // Sets multiple values if (typeof key === "object") { return this.each(function () { data_user.set(this, key); }); } return access(this, function (value) { var data, camelKey = jQuery.camelCase(key); // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if (elem && value === undefined) { // Attempt to get data from the cache // with the key as-is data = data_user.get(elem, key); if (data !== undefined) { return data; } // Attempt to get data from the cache // with the key camelized data = data_user.get(elem, camelKey); if (data !== undefined) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr(elem, camelKey, undefined); if (data !== undefined) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each(function () { // First, attempt to store a copy or reference of any // data that might've been store with a camelCased key. var data = data_user.get(this, camelKey); // For HTML5 data-* attribute interop, we have to // store property names with dashes in a camelCase form. // This might not apply to all properties...* data_user.set(this, camelKey, value); // *... In the case of properties that might _actually_ // have dashes, we need to also store a copy of that // unchanged property. if (key.indexOf("-") !== -1 && data !== undefined) { data_user.set(this, key, value); } }); }, null, value, arguments.length > 1, null, true); }, removeData: function (key) { return this.each(function () { data_user.remove(this, key); }); } }); jQuery.extend({ queue: function (elem, type, data) { var queue; if (elem) { type = ( type || "fx" ) + "queue"; queue = data_priv.get(elem, type); // Speed up dequeue by getting out quickly if this is just a lookup if (data) { if (!queue || jQuery.isArray(data)) { queue = data_priv.access(elem, type, jQuery.makeArray(data)); } else { queue.push(data); } } return queue || []; } }, dequeue: function (elem, type) { type = type || "fx"; var queue = jQuery.queue(elem, type), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks(elem, type), next = function () { jQuery.dequeue(elem, type); }; // If the fx queue is dequeued, always remove the progress sentinel if (fn === "inprogress") { fn = queue.shift(); startLength--; } if (fn) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if (type === "fx") { queue.unshift("inprogress"); } // clear up the last queue stop function delete hooks.stop; fn.call(elem, next, hooks); } if (!startLength && hooks) { hooks.empty.fire(); } }, // not intended for public consumption - generates a queueHooks object, or returns the current one _queueHooks: function (elem, type) { var key = type + "queueHooks"; return data_priv.get(elem, key) || data_priv.access(elem, key, { empty: jQuery.Callbacks("once memory").add(function () { data_priv.remove(elem, [ type + "queue", key ]); }) }); } }); jQuery.fn.extend({ queue: function (type, data) { var setter = 2; if (typeof type !== "string") { data = type; type = "fx"; setter--; } if (arguments.length < setter) { return jQuery.queue(this[0], type); } return data === undefined ? this : this.each(function () { var queue = jQuery.queue(this, type, data); // ensure a hooks for this queue jQuery._queueHooks(this, type); if (type === "fx" && queue[0] !== "inprogress") { jQuery.dequeue(this, type); } }); }, dequeue: function (type) { return this.each(function () { jQuery.dequeue(this, type); }); }, clearQueue: function (type) { return this.queue(type || "fx", []); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function (type, obj) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function () { if (!( --count )) { defer.resolveWith(elements, [ elements ]); } }; if (typeof type !== "string") { obj = type; type = undefined; } type = type || "fx"; while (i--) { tmp = data_priv.get(elements[ i ], type + "queueHooks"); if (tmp && tmp.empty) { count++; tmp.empty.add(resolve); } } resolve(); return defer.promise(obj); } }); var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var isHidden = function (elem, el) { // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; return jQuery.css(elem, "display") === "none" || !jQuery.contains(elem.ownerDocument, elem); }; var rcheckableType = (/^(?:checkbox|radio)$/i); (function () { var fragment = document.createDocumentFragment(), div = fragment.appendChild(document.createElement("div")); // #11217 - WebKit loses check when the name is after the checked attribute div.innerHTML = ""; // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 // old WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode(true).cloneNode(true).lastChild.checked; // Make sure textarea (and checkbox) defaultValue is properly cloned // Support: IE9-IE11+ div.innerHTML = ""; support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue; })(); var strundefined = typeof undefined; support.focusinBubbles = "onfocusin" in window; var rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; function returnTrue() { return true; } function returnFalse() { return false; } function safeActiveElement() { try { return document.activeElement; } catch (err) { } } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function (elem, types, handler, data, selector) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = data_priv.get(elem); // Don't attach events to noData or text/comment nodes (but allow plain objects) if (!elemData) { return; } // Caller can pass in an object of custom data in lieu of the handler if (handler.handler) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Make sure that the handler has a unique ID, used to find/remove it later if (!handler.guid) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if (!(events = elemData.events)) { events = elemData.events = {}; } if (!(eventHandle = elemData.handle)) { eventHandle = elemData.handle = function (e) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply(elem, arguments) : undefined; }; } // Handle multiple events separated by a space types = ( types || "" ).match(rnotwhite) || [ "" ]; t = types.length; while (t--) { tmp = rtypenamespace.exec(types[t]) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split(".").sort(); // There *must* be a type, no attaching namespace-only handlers if (!type) { continue; } // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test(selector), namespace: namespaces.join(".") }, handleObjIn); // Init the event handler queue if we're the first if (!(handlers = events[ type ])) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) { if (elem.addEventListener) { elem.addEventListener(type, eventHandle, false); } } } if (special.add) { special.add.call(elem, handleObj); if (!handleObj.handler.guid) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if (selector) { handlers.splice(handlers.delegateCount++, 0, handleObj); } else { handlers.push(handleObj); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } }, // Detach an event or set of events from an element remove: function (elem, types, handler, selector, mappedTypes) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = data_priv.hasData(elem) && data_priv.get(elem); if (!elemData || !(events = elemData.events)) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match(rnotwhite) || [ "" ]; t = types.length; while (t--) { tmp = rtypenamespace.exec(types[t]) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split(".").sort(); // Unbind all events (on this namespace, if provided) for the element if (!type) { for (type in events) { jQuery.event.remove(elem, type + types[ t ], handler, selector, true); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[2] && new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)"); // Remove matching events origCount = j = handlers.length; while (j--) { handleObj = handlers[ j ]; if (( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test(handleObj.namespace) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector )) { handlers.splice(j, 1); if (handleObj.selector) { handlers.delegateCount--; } if (special.remove) { special.remove.call(elem, handleObj); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if (origCount && !handlers.length) { if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) { jQuery.removeEvent(elem, type, elemData.handle); } delete events[ type ]; } } // Remove the expando if it's no longer used if (jQuery.isEmptyObject(events)) { delete elemData.handle; data_priv.remove(elem, "events"); } }, trigger: function (event, data, elem, onlyHandlers) { var i, cur, tmp, bubbleType, ontype, handle, special, eventPath = [ elem || document ], type = hasOwn.call(event, "type") ? event.type : event, namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if (elem.nodeType === 3 || elem.nodeType === 8) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if (rfocusMorph.test(type + jQuery.event.triggered)) { return; } if (type.indexOf(".") >= 0) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event(type, typeof event === "object" && event); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; // Clean up the event in case it is being reused event.result = undefined; if (!event.target) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray(data, [ event ]); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if (!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)) { bubbleType = special.delegateType || type; if (!rfocusMorph.test(bubbleType + type)) { cur = cur.parentNode; } for (; cur; cur = cur.parentNode) { eventPath.push(cur); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if (tmp === (elem.ownerDocument || document)) { eventPath.push(tmp.defaultView || tmp.parentWindow || window); } } // Fire handlers on the event path i = 0; while ((cur = eventPath[i++]) && !event.isPropagationStopped()) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( data_priv.get(cur, "events") || {} )[ event.type ] && data_priv.get(cur, "handle"); if (handle) { handle.apply(cur, data); } // Native handler handle = ontype && cur[ ontype ]; if (handle && handle.apply && jQuery.acceptData(cur)) { event.result = handle.apply(cur, data); if (event.result === false) { event.preventDefault(); } } } event.type = type; // If nobody prevented the default action, do it now if (!onlyHandlers && !event.isDefaultPrevented()) { if ((!special._default || special._default.apply(eventPath.pop(), data) === false) && jQuery.acceptData(elem)) { // Call a native DOM method on the target with the same name name as the event. // Don't do default actions on window, that's where global variables be (#6170) if (ontype && jQuery.isFunction(elem[ type ]) && !jQuery.isWindow(elem)) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if (tmp) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if (tmp) { elem[ ontype ] = tmp; } } } } return event.result; }, dispatch: function (event) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix(event); var i, j, ret, matched, handleObj, handlerQueue = [], args = slice.call(arguments), handlers = ( data_priv.get(this, "events") || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if (special.preDispatch && special.preDispatch.call(this, event) === false) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call(this, event, handlers); // Run delegates first; they may want to stop propagation beneath us i = 0; while ((matched = handlerQueue[ i++ ]) && !event.isPropagationStopped()) { event.currentTarget = matched.elem; j = 0; while ((handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped()) { // Triggered event must either 1) have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if (!event.namespace_re || event.namespace_re.test(handleObj.namespace)) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply(matched.elem, args); if (ret !== undefined) { if ((event.result = ret) === false) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if (special.postDispatch) { special.postDispatch.call(this, event); } return event.result; }, handlers: function (event, handlers) { var i, matches, sel, handleObj, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers // Black-hole SVG instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) if (delegateCount && cur.nodeType && (!event.button || event.type !== "click")) { for (; cur !== this; cur = cur.parentNode || this) { // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if (cur.disabled !== true || event.type !== "click") { matches = []; for (i = 0; i < delegateCount; i++) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if (matches[ sel ] === undefined) { matches[ sel ] = handleObj.needsContext ? jQuery(sel, this).index(cur) >= 0 : jQuery.find(sel, this, null, [ cur ]).length; } if (matches[ sel ]) { matches.push(handleObj); } } if (matches.length) { handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers if (delegateCount < handlers.length) { handlerQueue.push({ elem: this, handlers: handlers.slice(delegateCount) }); } return handlerQueue; }, // Includes some event props shared by KeyEvent and MouseEvent props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function (event, original) { // Add which for key events if (event.which == null) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; } }, mouseHooks: { props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function (event, original) { var eventDoc, doc, body, button = original.button; // Calculate pageX/Y if missing and clientX/Y available if (event.pageX == null && original.clientX != null) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add which for click: 1 === left; 2 === middle; 3 === right // Note: button is not normalized, so don't use it if (!event.which && button !== undefined) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; } }, fix: function (event) { if (event[ jQuery.expando ]) { return event; } // Create a writable copy of the event object and normalize some properties var i, prop, copy, type = event.type, originalEvent = event, fixHook = this.fixHooks[ type ]; if (!fixHook) { this.fixHooks[ type ] = fixHook = rmouseEvent.test(type) ? this.mouseHooks : rkeyEvent.test(type) ? this.keyHooks : {}; } copy = fixHook.props ? this.props.concat(fixHook.props) : this.props; event = new jQuery.Event(originalEvent); i = copy.length; while (i--) { prop = copy[ i ]; event[ prop ] = originalEvent[ prop ]; } // Support: Cordova 2.5 (WebKit) (#13255) // All events should have a target; Cordova deviceready doesn't if (!event.target) { event.target = document; } // Support: Safari 6.0+, Chrome < 28 // Target should not be a text node (#504, #13143) if (event.target.nodeType === 3) { event.target = event.target.parentNode; } return fixHook.filter ? fixHook.filter(event, originalEvent) : event; }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { // Fire native event if possible so blur/focus sequence is correct trigger: function () { if (this !== safeActiveElement() && this.focus) { this.focus(); return false; } }, delegateType: "focusin" }, blur: { trigger: function () { if (this === safeActiveElement() && this.blur) { this.blur(); return false; } }, delegateType: "focusout" }, click: { // For checkbox, fire native event so checked state will be right trigger: function () { if (this.type === "checkbox" && this.click && jQuery.nodeName(this, "input")) { this.click(); return false; } }, // For cross-browser consistency, don't fire native .click() on links _default: function (event) { return jQuery.nodeName(event.target, "a"); } }, beforeunload: { postDispatch: function (event) { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. if (event.result !== undefined) { event.originalEvent.returnValue = event.result; } } } }, simulate: function (type, elem, event, bubble) { // Piggyback on a donor event to simulate a different one. // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); if (bubble) { jQuery.event.trigger(e, null, elem); } else { jQuery.event.dispatch.call(elem, e); } if (e.isDefaultPrevented()) { event.preventDefault(); } } }; jQuery.removeEvent = function (elem, type, handle) { if (elem.removeEventListener) { elem.removeEventListener(type, handle, false); } }; jQuery.Event = function (src, props) { // Allow instantiation without the 'new' keyword if (!(this instanceof jQuery.Event)) { return new jQuery.Event(src, props); } // Event object if (src && src.type) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || // Support: Android < 4.0 src.defaultPrevented === undefined && src.getPreventDefault && src.getPreventDefault() ? returnTrue : returnFalse; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if (props) { jQuery.extend(this, props); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function () { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if (e && e.preventDefault) { e.preventDefault(); } }, stopPropagation: function () { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if (e && e.stopPropagation) { e.stopPropagation(); } }, stopImmediatePropagation: function () { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); } }; // Create mouseenter/leave events using mouseover/out and event-time checks // Support: Chrome 15+ jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function (orig, fix) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function (event) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if (!related || (related !== target && !jQuery.contains(target, related))) { event.type = handleObj.origType; ret = handleObj.handler.apply(this, arguments); event.type = fix; } return ret; } }; }); // Create "bubbling" focus and blur events // Support: Firefox, Chrome, Safari if (!support.focusinBubbles) { jQuery.each({ focus: "focusin", blur: "focusout" }, function (orig, fix) { // Attach a single capturing handler on the document while someone wants focusin/focusout var handler = function (event) { jQuery.event.simulate(fix, event.target, jQuery.event.fix(event), true); }; jQuery.event.special[ fix ] = { setup: function () { var doc = this.ownerDocument || this, attaches = data_priv.access(doc, fix); if (!attaches) { doc.addEventListener(orig, handler, true); } data_priv.access(doc, fix, ( attaches || 0 ) + 1); }, teardown: function () { var doc = this.ownerDocument || this, attaches = data_priv.access(doc, fix) - 1; if (!attaches) { doc.removeEventListener(orig, handler, true); data_priv.remove(doc, fix); } else { data_priv.access(doc, fix, attaches); } } }; }); } jQuery.fn.extend({ on: function (types, selector, data, fn, /*INTERNAL*/ one) { var origFn, type; // Types can be a map of types/handlers if (typeof types === "object") { // ( types-Object, selector, data ) if (typeof selector !== "string") { // ( types-Object, data ) data = data || selector; selector = undefined; } for (type in types) { this.on(type, selector, data, types[ type ], one); } return this; } if (data == null && fn == null) { // ( types, fn ) fn = selector; data = selector = undefined; } else if (fn == null) { if (typeof selector === "string") { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if (fn === false) { fn = returnFalse; } else if (!fn) { return this; } if (one === 1) { origFn = fn; fn = function (event) { // Can use an empty set, since event contains the info jQuery().off(event); return origFn.apply(this, arguments); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return this.each(function () { jQuery.event.add(this, types, fn, data, selector); }); }, one: function (types, selector, data, fn) { return this.on(types, selector, data, fn, 1); }, off: function (types, selector, fn) { var handleObj, type; if (types && types.preventDefault && types.handleObj) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery(types.delegateTarget).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if (typeof types === "object") { // ( types-object [, selector] ) for (type in types) { this.off(type, selector, types[ type ]); } return this; } if (selector === false || typeof selector === "function") { // ( types [, fn] ) fn = selector; selector = undefined; } if (fn === false) { fn = returnFalse; } return this.each(function () { jQuery.event.remove(this, types, fn, selector); }); }, trigger: function (type, data) { return this.each(function () { jQuery.event.trigger(type, data, this); }); }, triggerHandler: function (type, data) { var elem = this[0]; if (elem) { return jQuery.event.trigger(type, data, elem, true); } } }); var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, rtagName = /<([\w:]+)/, rhtml = /<|&#?\w+;/, rnoInnerhtml = /<(?:script|style|link)/i, // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptType = /^$|\/(?:java|ecma)script/i, rscriptTypeMasked = /^true\/(.*)/, rcleanScript = /^\s*\s*$/g, // We have to close these tags to support XHTML (#13200) wrapMap = { // Support: IE 9 option: [ 1, "" ], thead: [ 1, "", "
" ], col: [ 2, "", "
" ], tr: [ 2, "", "
" ], td: [ 3, "", "
" ], _default: [ 0, "", "" ] }; // Support: IE 9 wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; // Support: 1.x compatibility // Manipulating tables requires a tbody function manipulationTarget(elem, content) { return jQuery.nodeName(elem, "table") && jQuery.nodeName(content.nodeType !== 11 ? content : content.firstChild, "tr") ? elem.getElementsByTagName("tbody")[0] || elem.appendChild(elem.ownerDocument.createElement("tbody")) : elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript(elem) { elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; return elem; } function restoreScript(elem) { var match = rscriptTypeMasked.exec(elem.type); if (match) { elem.type = match[ 1 ]; } else { elem.removeAttribute("type"); } return elem; } // Mark scripts as having already been evaluated function setGlobalEval(elems, refElements) { var i = 0, l = elems.length; for (; i < l; i++) { data_priv.set( elems[ i ], "globalEval", !refElements || data_priv.get(refElements[ i ], "globalEval") ); } } function cloneCopyEvent(src, dest) { var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if (dest.nodeType !== 1) { return; } // 1. Copy private data: events, handlers, etc. if (data_priv.hasData(src)) { pdataOld = data_priv.access(src); pdataCur = data_priv.set(dest, pdataOld); events = pdataOld.events; if (events) { delete pdataCur.handle; pdataCur.events = {}; for (type in events) { for (i = 0, l = events[ type ].length; i < l; i++) { jQuery.event.add(dest, type, events[ type ][ i ]); } } } } // 2. Copy user data if (data_user.hasData(src)) { udataOld = data_user.access(src); udataCur = jQuery.extend({}, udataOld); data_user.set(dest, udataCur); } } function getAll(context, tag) { var ret = context.getElementsByTagName ? context.getElementsByTagName(tag || "*") : context.querySelectorAll ? context.querySelectorAll(tag || "*") : []; return tag === undefined || tag && jQuery.nodeName(context, tag) ? jQuery.merge([ context ], ret) : ret; } // Support: IE >= 9 function fixInput(src, dest) { var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button. if (nodeName === "input" && rcheckableType.test(src.type)) { dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options } else if (nodeName === "input" || nodeName === "textarea") { dest.defaultValue = src.defaultValue; } } jQuery.extend({ clone: function (elem, dataAndEvents, deepDataAndEvents) { var i, l, srcElements, destElements, clone = elem.cloneNode(true), inPage = jQuery.contains(elem.ownerDocument, elem); // Support: IE >= 9 // Fix Cloning issues if (!support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc(elem)) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll(clone); srcElements = getAll(elem); for (i = 0, l = srcElements.length; i < l; i++) { fixInput(srcElements[ i ], destElements[ i ]); } } // Copy the events from the original to the clone if (dataAndEvents) { if (deepDataAndEvents) { srcElements = srcElements || getAll(elem); destElements = destElements || getAll(clone); for (i = 0, l = srcElements.length; i < l; i++) { cloneCopyEvent(srcElements[ i ], destElements[ i ]); } } else { cloneCopyEvent(elem, clone); } } // Preserve script evaluation history destElements = getAll(clone, "script"); if (destElements.length > 0) { setGlobalEval(destElements, !inPage && getAll(elem, "script")); } // Return the cloned set return clone; }, buildFragment: function (elems, context, scripts, selection) { var elem, tmp, tag, wrap, contains, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, l = elems.length; for (; i < l; i++) { elem = elems[ i ]; if (elem || elem === 0) { // Add nodes directly if (jQuery.type(elem) === "object") { // Support: QtWebKit // jQuery.merge because push.apply(_, arraylike) throws jQuery.merge(nodes, elem.nodeType ? [ elem ] : elem); // Convert non-html into a text node } else if (!rhtml.test(elem)) { nodes.push(context.createTextNode(elem)); // Convert html into DOM nodes } else { tmp = tmp || fragment.appendChild(context.createElement("div")); // Deserialize a standard representation tag = ( rtagName.exec(elem) || [ "", "" ] )[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[ 1 ] + elem.replace(rxhtmlTag, "<$1>") + wrap[ 2 ]; // Descend through wrappers to the right content j = wrap[ 0 ]; while (j--) { tmp = tmp.lastChild; } // Support: QtWebKit // jQuery.merge because push.apply(_, arraylike) throws jQuery.merge(nodes, tmp.childNodes); // Remember the top-level container tmp = fragment.firstChild; // Fixes #12346 // Support: Webkit, IE tmp.textContent = ""; } } } // Remove wrapper from fragment fragment.textContent = ""; i = 0; while ((elem = nodes[ i++ ])) { // #4087 - If origin and destination elements are the same, and this is // that element, do not do anything if (selection && jQuery.inArray(elem, selection) !== -1) { continue; } contains = jQuery.contains(elem.ownerDocument, elem); // Append to fragment tmp = getAll(fragment.appendChild(elem), "script"); // Preserve script evaluation history if (contains) { setGlobalEval(tmp); } // Capture executables if (scripts) { j = 0; while ((elem = tmp[ j++ ])) { if (rscriptType.test(elem.type || "")) { scripts.push(elem); } } } } return fragment; }, cleanData: function (elems) { var data, elem, events, type, key, j, special = jQuery.event.special, i = 0; for (; (elem = elems[ i ]) !== undefined; i++) { if (jQuery.acceptData(elem)) { key = elem[ data_priv.expando ]; if (key && (data = data_priv.cache[ key ])) { events = Object.keys(data.events || {}); if (events.length) { for (j = 0; (type = events[j]) !== undefined; j++) { if (special[ type ]) { jQuery.event.remove(elem, type); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent(elem, type, data.handle); } } } if (data_priv.cache[ key ]) { // Discard any remaining `private` data delete data_priv.cache[ key ]; } } } // Discard any remaining `user` data delete data_user.cache[ elem[ data_user.expando ] ]; } } }); jQuery.fn.extend({ text: function (value) { return access(this, function (value) { return value === undefined ? jQuery.text(this) : this.empty().each(function () { if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) { this.textContent = value; } }); }, null, value, arguments.length); }, append: function () { return this.domManip(arguments, function (elem) { if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) { var target = manipulationTarget(this, elem); target.appendChild(elem); } }); }, prepend: function () { return this.domManip(arguments, function (elem) { if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) { var target = manipulationTarget(this, elem); target.insertBefore(elem, target.firstChild); } }); }, before: function () { return this.domManip(arguments, function (elem) { if (this.parentNode) { this.parentNode.insertBefore(elem, this); } }); }, after: function () { return this.domManip(arguments, function (elem) { if (this.parentNode) { this.parentNode.insertBefore(elem, this.nextSibling); } }); }, remove: function (selector, keepData /* Internal Use Only */) { var elem, elems = selector ? jQuery.filter(selector, this) : this, i = 0; for (; (elem = elems[i]) != null; i++) { if (!keepData && elem.nodeType === 1) { jQuery.cleanData(getAll(elem)); } if (elem.parentNode) { if (keepData && jQuery.contains(elem.ownerDocument, elem)) { setGlobalEval(getAll(elem, "script")); } elem.parentNode.removeChild(elem); } } return this; }, empty: function () { var elem, i = 0; for (; (elem = this[i]) != null; i++) { if (elem.nodeType === 1) { // Prevent memory leaks jQuery.cleanData(getAll(elem, false)); // Remove any remaining nodes elem.textContent = ""; } } return this; }, clone: function (dataAndEvents, deepDataAndEvents) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map(function () { return jQuery.clone(this, dataAndEvents, deepDataAndEvents); }); }, html: function (value) { return access(this, function (value) { var elem = this[ 0 ] || {}, i = 0, l = this.length; if (value === undefined && elem.nodeType === 1) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if (typeof value === "string" && !rnoInnerhtml.test(value) && !wrapMap[ ( rtagName.exec(value) || [ "", "" ] )[ 1 ].toLowerCase() ]) { value = value.replace(rxhtmlTag, "<$1>"); try { for (; i < l; i++) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if (elem.nodeType === 1) { jQuery.cleanData(getAll(elem, false)); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch (e) { } } if (elem) { this.empty().append(value); } }, null, value, arguments.length); }, replaceWith: function () { var arg = arguments[ 0 ]; // Make the changes, replacing each context element with the new content this.domManip(arguments, function (elem) { arg = this.parentNode; jQuery.cleanData(getAll(this)); if (arg) { arg.replaceChild(elem, this); } }); // Force removal if there was no new content (e.g., from empty arguments) return arg && (arg.length || arg.nodeType) ? this : this.remove(); }, detach: function (selector) { return this.remove(selector, true); }, domManip: function (args, callback) { // Flatten any nested arrays args = concat.apply([], args); var fragment, first, scripts, hasScripts, node, doc, i = 0, l = this.length, set = this, iNoClone = l - 1, value = args[ 0 ], isFunction = jQuery.isFunction(value); // We can't cloneNode fragments that contain checked, in WebKit if (isFunction || ( l > 1 && typeof value === "string" && !support.checkClone && rchecked.test(value) )) { return this.each(function (index) { var self = set.eq(index); if (isFunction) { args[ 0 ] = value.call(this, index, self.html()); } self.domManip(args, callback); }); } if (l) { fragment = jQuery.buildFragment(args, this[ 0 ].ownerDocument, false, this); first = fragment.firstChild; if (fragment.childNodes.length === 1) { fragment = first; } if (first) { scripts = jQuery.map(getAll(fragment, "script"), disableScript); hasScripts = scripts.length; // Use the original fragment for the last item instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for (; i < l; i++) { node = fragment; if (i !== iNoClone) { node = jQuery.clone(node, true, true); // Keep references to cloned scripts for later restoration if (hasScripts) { // Support: QtWebKit // jQuery.merge because push.apply(_, arraylike) throws jQuery.merge(scripts, getAll(node, "script")); } } callback.call(this[ i ], node, i); } if (hasScripts) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map(scripts, restoreScript); // Evaluate executable scripts on first document insertion for (i = 0; i < hasScripts; i++) { node = scripts[ i ]; if (rscriptType.test(node.type || "") && !data_priv.access(node, "globalEval") && jQuery.contains(doc, node)) { if (node.src) { // Optional AJAX dependency, but won't run scripts if not present if (jQuery._evalUrl) { jQuery._evalUrl(node.src); } } else { jQuery.globalEval(node.textContent.replace(rcleanScript, "")); } } } } } } return this; } }); jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function (name, original) { jQuery.fn[ name ] = function (selector) { var elems, ret = [], insert = jQuery(selector), last = insert.length - 1, i = 0; for (; i <= last; i++) { elems = i === last ? this : this.clone(true); jQuery(insert[ i ])[ original ](elems); // Support: QtWebKit // .get() because push.apply(_, arraylike) throws push.apply(ret, elems.get()); } return this.pushStack(ret); }; }); var iframe, elemdisplay = {}; /** * Retrieve the actual display of a element * @param {String} name nodeName of the element * @param {Object} doc Document object */ // Called only from within defaultDisplay function actualDisplay(name, doc) { var elem = jQuery(doc.createElement(name)).appendTo(doc.body), // getDefaultComputedStyle might be reliably used only on attached element display = window.getDefaultComputedStyle ? // Use of this method is a temporary fix (more like optmization) until something better comes along, // since it was removed from specification and supported only in FF window.getDefaultComputedStyle(elem[ 0 ]).display : jQuery.css(elem[ 0 ], "display"); // We don't have any data stored on the element, // so use "detach" method as fast way to get rid of the element elem.detach(); return display; } /** * Try to determine the default display value of an element * @param {String} nodeName */ function defaultDisplay(nodeName) { var doc = document, display = elemdisplay[ nodeName ]; if (!display) { display = actualDisplay(nodeName, doc); // If the simple way fails, read from inside an iframe if (display === "none" || !display) { // Use the already-created iframe if possible iframe = (iframe || jQuery("