├── log └── .keep ├── app ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── concerns │ │ └── .keep │ ├── photo.rb │ ├── comment.rb │ └── article.rb ├── assets │ ├── images │ │ ├── .keep │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ ├── 7.jpg │ │ ├── 8.jpg │ │ ├── bg.png │ │ ├── rss.png │ │ ├── pic1.jpg │ │ ├── CZ16VX4.gif │ │ ├── images.jpeg │ │ ├── search.gif │ │ └── fork_me_on_github.png │ ├── stylesheets │ │ ├── articles.scss │ │ ├── application.scss │ │ ├── admin │ │ │ └── posts.scss │ │ ├── highlight.css │ │ ├── markdown.scss │ │ ├── welcome.scss │ │ └── variables.scss │ └── javascripts │ │ ├── application.js │ │ ├── admin │ │ ├── photos.coffee │ │ └── posts.coffee │ │ ├── photo.coffee │ │ ├── googleanalytics.coffee │ │ └── jquery.atwho.js ├── views │ ├── articles │ │ ├── jia.html │ │ ├── index.html.slim │ │ ├── search.html.slim │ │ └── show.html.slim │ ├── demos │ │ └── index.html.slim │ ├── kaminari │ │ ├── _gap.html.slim │ │ ├── _first_page.html.slim │ │ ├── _last_page.html.slim │ │ ├── _next_page.html.slim │ │ ├── _prev_page.html.slim │ │ ├── _page.html.slim │ │ └── _paginator.html.slim │ ├── admin │ │ ├── articles │ │ │ ├── edit.html.slim │ │ │ ├── new.html.slim │ │ │ ├── index.html.slim │ │ │ └── _form.html.slim │ │ ├── sessions │ │ │ └── new.html.slim │ │ └── photos │ │ │ └── index.html.slim │ ├── welcome │ │ ├── about.html.slim │ │ ├── photo.html.slim │ │ ├── index.html.slim │ │ └── rss.builder │ ├── shared │ │ └── _form_error_message.html.slim │ └── layouts │ │ ├── photo.html.slim │ │ ├── admin.html.slim │ │ └── application.html.slim ├── controllers │ ├── concerns │ │ └── .keep │ ├── demos_controller.rb │ ├── application_controller.rb │ ├── admin │ │ ├── sessions_controller.rb │ │ ├── photos_controller.rb │ │ └── articles_controller.rb │ ├── comments_controller.rb │ ├── welcome_controller.rb │ └── articles_controller.rb ├── helpers │ ├── welcome_helper.rb │ ├── articles_helper.rb │ ├── comments_helper.rb │ ├── admin │ │ ├── posts_helper.rb │ │ └── sessions_helper.rb │ └── application_helper.rb └── uploaders │ └── image_uploader.rb ├── lib ├── assets │ └── .keep ├── tasks │ └── .keep └── markdown.rb ├── public ├── favicon.ico ├── audios │ └── sound.mp3 ├── img │ └── prev_next.png ├── uploads │ └── photo │ │ └── image │ │ └── 0 │ │ ├── photo01.jpg │ │ ├── photo02.jpg │ │ ├── photo03.jpg │ │ ├── photo04.jpg │ │ ├── photo05.jpg │ │ ├── photo06.jpg │ │ ├── photo07.jpg │ │ ├── photo08.jpg │ │ ├── photo09.jpg │ │ ├── photo10.jpg │ │ ├── photo12.jpg │ │ ├── photo13.jpg │ │ ├── photo14.jpg │ │ ├── photo15.jpg │ │ ├── photo16.jpg │ │ ├── photo17.jpg │ │ ├── photo18.jpg │ │ ├── photo20.jpg │ │ ├── photo21.jpg │ │ ├── photo22.jpg │ │ ├── photo23.jpg │ │ ├── photo24.jpg │ │ ├── photo25.jpg │ │ ├── photo26.jpg │ │ └── photo30.jpg ├── robots.txt ├── sitemap.xml ├── 500.html ├── 422.html ├── 404.html └── jia.html ├── test ├── helpers │ └── .keep ├── mailers │ └── .keep ├── models │ └── .keep ├── controllers │ └── .keep ├── fixtures │ └── .keep ├── integration │ └── .keep └── test_helper.rb ├── vendor └── assets │ ├── javascripts │ ├── .keep │ ├── mp3.js │ ├── jquery.tiles.js │ └── jquery.html5-fileupload.js │ └── stylesheets │ ├── .keep │ └── jquery.tiles.min.css ├── config ├── initializers │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── assets.rb │ ├── truncate_html.rb │ ├── session_store.rb │ ├── mime_types.rb │ ├── kaminari_config.rb │ ├── wrap_parameters.rb │ └── inflections.rb ├── boot.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb ├── database.yml ├── nigix.conf ├── locales │ └── en.yml ├── unicorn │ └── production.rb ├── routes.rb ├── secrets.yml ├── application.rb └── deploy.rb ├── .ackrc ├── config.ru ├── Rakefile ├── bin ├── rake ├── bundle ├── rails └── setup ├── db ├── migrate │ ├── 20150324104912_add_article_id_to_comment.rb │ ├── 20160224023054_add_photo_type_to_photo.rb │ ├── 20160224042413_add_self_info_to_article.rb │ └── 20150326142916_create_photos.rb ├── seeds.rb └── schema.rb ├── .gitignore ├── Gemfile ├── README.md └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/articles/jia.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/demos/index.html.slim: -------------------------------------------------------------------------------- 1 | p 敬请期待 2 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/welcome_helper.rb: -------------------------------------------------------------------------------- 1 | module WelcomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/articles_helper.rb: -------------------------------------------------------------------------------- 1 | module ArticlesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/comments_helper.rb: -------------------------------------------------------------------------------- 1 | module CommentsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/admin/posts_helper.rb: -------------------------------------------------------------------------------- 1 | module Admin::PostsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /.ackrc: -------------------------------------------------------------------------------- 1 | --ignore-file=ext:log 2 | --ignore-dir=public 3 | --ignore-dir=tmp 4 | -------------------------------------------------------------------------------- /app/helpers/admin/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module Admin::SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/demos_controller.rb: -------------------------------------------------------------------------------- 1 | class DemosController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require ::File.expand_path('../config/environment', __FILE__) 2 | run Rails.application 3 | -------------------------------------------------------------------------------- /app/assets/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/1.jpg -------------------------------------------------------------------------------- /app/assets/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/2.jpg -------------------------------------------------------------------------------- /app/assets/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/3.jpg -------------------------------------------------------------------------------- /app/assets/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/4.jpg -------------------------------------------------------------------------------- /app/assets/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/5.jpg -------------------------------------------------------------------------------- /app/assets/images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/6.jpg -------------------------------------------------------------------------------- /app/assets/images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/7.jpg -------------------------------------------------------------------------------- /app/assets/images/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/8.jpg -------------------------------------------------------------------------------- /app/assets/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/bg.png -------------------------------------------------------------------------------- /app/assets/images/rss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/rss.png -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.slim: -------------------------------------------------------------------------------- 1 | li.disabled 2 | = link_to raw(t 'views.pagination.truncate'), '#' 3 | -------------------------------------------------------------------------------- /public/audios/sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/audios/sound.mp3 -------------------------------------------------------------------------------- /public/img/prev_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/img/prev_next.png -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require File.expand_path('../config/application', __FILE__) 2 | 3 | Rails.application.load_tasks 4 | -------------------------------------------------------------------------------- /app/assets/images/pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/pic1.jpg -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.action_dispatch.cookies_serializer = :json 2 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.filter_parameters += [:password] 2 | -------------------------------------------------------------------------------- /app/assets/images/CZ16VX4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/CZ16VX4.gif -------------------------------------------------------------------------------- /app/assets/images/images.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/images.jpeg -------------------------------------------------------------------------------- /app/assets/images/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/search.gif -------------------------------------------------------------------------------- /app/views/admin/articles/edit.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2.article-new 3 | |编辑博客 4 | 5 | = render 'form' 6 | -------------------------------------------------------------------------------- /app/views/admin/articles/new.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2.article-new 3 | |新建博客 4 | 5 | = render 'form' 6 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /app/assets/images/fork_me_on_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/app/assets/images/fork_me_on_github.png -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo01.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo02.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo03.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo04.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo05.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo06.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo07.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo08.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo09.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo10.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo12.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo13.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo14.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo15.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo16.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo17.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo18.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo20.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo21.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo22.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo23.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo24.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo25.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo26.jpg -------------------------------------------------------------------------------- /public/uploads/photo/image/0/photo30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhenangel/Rina_Blog/HEAD/public/uploads/photo/image/0/photo30.jpg -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.assets.version = '1.0' 2 | Rails.application.config.assets.precompile += %w( mp3.js ) 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.first'), 3 | url, remote: remote 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.last'), 3 | url, remote: remote 4 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /config/initializers/truncate_html.rb: -------------------------------------------------------------------------------- 1 | TruncateHtml.configure do |config| 2 | config.length = 200 3 | config.omission = '...' 4 | end 5 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.next'), 3 | url, rel: 'next', remote: remote 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), 3 | url, rel: 'prev', remote: remote 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_Rina_Blog_session' 4 | -------------------------------------------------------------------------------- /app/views/welcome/about.html.slim: -------------------------------------------------------------------------------- 1 | - content_for(:title) do 2 | |关于 - 个人资料 3 | 4 | h2 5 | = @self_info.title 6 | .show-background 7 | .content.markdown 8 | == @self_info.content_html 9 | -------------------------------------------------------------------------------- /db/migrate/20150324104912_add_article_id_to_comment.rb: -------------------------------------------------------------------------------- 1 | class AddArticleIdToComment < ActiveRecord::Migration 2 | def change 3 | add_column :comments, :article_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.slim: -------------------------------------------------------------------------------- 1 | li class="#{'active' if page.current?}" 2 | = link_to page, page.current? ? '#' : url, 3 | remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil 4 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /db/migrate/20160224023054_add_photo_type_to_photo.rb: -------------------------------------------------------------------------------- 1 | class AddPhotoTypeToPhoto < ActiveRecord::Migration 2 | def change 3 | add_column :photos, :photo_type, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160224042413_add_self_info_to_article.rb: -------------------------------------------------------------------------------- 1 | class AddSelfInfoToArticle < ActiveRecord::Migration 2 | def change 3 | add_column :articles, :self_info, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/articles.scss: -------------------------------------------------------------------------------- 1 | .search-content{ 2 | margin: 20px 8px 3px 4px; 3 | } 4 | 5 | img{ 6 | width: 848px; 7 | } 8 | 9 | .pagination{ 10 | margin: 80px 0; 11 | } 12 | 13 | em{ 14 | color: #c00; 15 | } 16 | -------------------------------------------------------------------------------- /app/models/photo.rb: -------------------------------------------------------------------------------- 1 | class Photo < ActiveRecord::Base 2 | mount_uploader :image, ImageUploader 3 | 4 | scope :photo_album, -> { where(photo_type: true) } 5 | 6 | def image_name 7 | image.file.filename 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | User-agent: * 5 | Disallow: /admin 6 | -------------------------------------------------------------------------------- /db/migrate/20150326142916_create_photos.rb: -------------------------------------------------------------------------------- 1 | class CreatePhotos < ActiveRecord::Migration 2 | def change 3 | create_table :photos do |t| 4 | t.string :image 5 | 6 | t.timestamps null: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/articles/index.html.slim: -------------------------------------------------------------------------------- 1 | - content_for(:title) do 2 | |所有 - 所有文章列表 3 | 4 | - @articles.each do |article| 5 | .row.all-article 6 | .col-md-4 7 | = link_to article.title, article 8 | .col-md-4 9 | = article.created_at.to_date 10 | -------------------------------------------------------------------------------- /app/models/comment.rb: -------------------------------------------------------------------------------- 1 | class Comment < ActiveRecord::Base 2 | belongs_to :article 3 | 4 | validates :message, :username, :email, presence: true 5 | validates :username, length: { maximum: 20 } 6 | validates_format_of :email,:with => /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/ 7 | end 8 | -------------------------------------------------------------------------------- /app/views/articles/search.html.slim: -------------------------------------------------------------------------------- 1 | .search-content 2 | = render 'shared/form_error_message' 3 | - @articles.each do |article| 4 | h2.search-title 5 | = link_to sanitize(article.title), article 6 | .search-html 7 | = sanitize article.content 8 | = paginate @articles 9 | -------------------------------------------------------------------------------- /lib/markdown.rb: -------------------------------------------------------------------------------- 1 | require 'rouge' 2 | require 'rouge/plugins/redcarpet' 3 | 4 | class CodeHTML < Redcarpet::Render::HTML 5 | include Rouge::Plugins::Redcarpet 6 | 7 | def initialize(extensions = {}) 8 | super extensions.merge(link_attributes: { target: "_blank" }) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery 2 | //= require bootstrap-sprockets 3 | //= require jquery_ujs 4 | //= require turbolinks 5 | //= require nprogress 6 | //= require nprogress-turbolinks 7 | //= require jquery.html5-fileupload 8 | //= require jquery.tiles 9 | //= require_tree . 10 | -------------------------------------------------------------------------------- /config/initializers/kaminari_config.rb: -------------------------------------------------------------------------------- 1 | Kaminari.configure do |config| 2 | config.default_per_page = 10 3 | # config.max_per_page = nil 4 | config.window = 2 5 | # config.outer_window = 0 6 | # config.left = 0 7 | # config.right = 0 8 | # config.page_method_name = :page 9 | # config.param_name = :page 10 | end 11 | -------------------------------------------------------------------------------- /app/views/shared/_form_error_message.html.slim: -------------------------------------------------------------------------------- 1 | - flash.each do |name, msg| 2 | - if msg.is_a?(String) 3 | div class="alert alert-dismissible alert-#{name == 'error'? 'danger':'info'}" role='alert' 4 | button.close type='button' data-dismiss="alert" aria-label="Close" 5 | span aria-hidden="true" 6 | |× 7 | = msg 8 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | 4 | before_action do 5 | @newest_articles = Article.order(created_at: 'desc').limit(10) 6 | end 7 | 8 | helper_method :login? 9 | def login? 10 | session[:login].present? 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import "bootstrap-sprockets"; 3 | @import "bootstrap"; 4 | @import 'admin/posts'; 5 | @import 'articles'; 6 | @import 'ihover'; 7 | @import 'welcome'; 8 | @import 'markdown'; 9 | @import 'highlight'; 10 | @import 'jquery.tiles.min'; 11 | @import 'nprogress'; 12 | @import 'nprogress-bootstrap'; 13 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | -------------------------------------------------------------------------------- /app/views/welcome/photo.html.slim: -------------------------------------------------------------------------------- 1 | = render 'shared/form_error_message' 2 | 3 | section 4 | .title 5 | button.start.action type="button" Play 6 | button.stop.action type="button" Stop 7 | .slider-wrap 8 | .slider 9 | - @photos.each do |photo| 10 | = image_tag(photo.image_url, size: '940*400') 11 | p = photo.image_name 12 | footer 13 | = link_to '返回首页', '/' 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/admin/photos.coffee: -------------------------------------------------------------------------------- 1 | $(document).on 'ready, page:change', -> 2 | $('#upload-photo-album').click -> 3 | $("input[type=file]").show().focus().click().hide() 4 | false 5 | 6 | opt = 7 | type: 'POST' 8 | url: "/admin/photos/upload" 9 | success: (data,status,xhr)-> 10 | location.reload() 11 | 12 | $('input.file-window-photo-album').fileUpload(opt) 13 | -------------------------------------------------------------------------------- /app/views/welcome/index.html.slim: -------------------------------------------------------------------------------- 1 | - content_for(:title) do 2 | |主页 - 最新文章列表 3 | .row.index-row 4 | - @articles.each_with_index do |artc1, index| 5 | .col-sm-6 6 | .ih-item.square.effect3.bottom_to_top.index-row 7 | .index-title 8 | = artc1.title 9 | = link_to article_path(artc1) do 10 | .img 11 | = image_tag("#{index + 1}.jpg") 12 | .info 13 | h3 = artc1.title 14 | p.square-p 点击查看 15 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.slim: -------------------------------------------------------------------------------- 1 | = paginator.render do 2 | ul.pagination 3 | == first_page_tag unless current_page.first? 4 | == prev_page_tag unless current_page.first? 5 | 6 | - each_page do |page| 7 | - if page.left_outer? || page.right_outer? || page.inside_window? 8 | == page_tag page 9 | - elsif !page.was_truncated? 10 | == gap_tag 11 | 12 | == next_page_tag unless current_page.last? 13 | == last_page_tag unless current_page.last? 14 | -------------------------------------------------------------------------------- /app/views/admin/articles/index.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | - @articles.each do |article| 3 | .row 4 | .col-md-3 5 | = link_to article.title, article 6 | .col-md-2 7 | = article.created_at.to_date 8 | .col-md-1 9 | = link_to "评论", article 10 | .col-md-1 11 | = link_to "编辑", edit_admin_article_path(article) 12 | .col-md-1 13 | = link_to "删除", admin_article_path(article), method: :delete, data: { confirm: "确认删除?" } 14 | 15 | = paginate @articles 16 | -------------------------------------------------------------------------------- /app/views/admin/sessions/new.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | .col-sm-6.col-sm-offset-3 4 | = render 'shared/form_error_message' 5 | h1 登录 6 | = form_for(:session, url: signin_path, class: 'form-horizontal') do |f| 7 | .form-group 8 | = f.text_field :username, class: 'form-control', placeholder: '用户名' 9 | .form-group 10 | = f.password_field :password, class: 'form-control', placeholder: '密码' 11 | = f.submit '登录', class: 'btn btn-large btn-primary' 12 | -------------------------------------------------------------------------------- /app/views/layouts/photo.html.slim: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title 4 | |Rina's Blog 5 | meta name="viewport" content="width=device-width, initial-scale=1.0, max-scale=1.0" 6 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true 7 | = javascript_include_tag 'application', 'data-turbolinks-track' => true 8 | = csrf_meta_tags 9 | body.photo-body 10 | .container.photo-container 11 | = audio_tag("sound.mp3", loop: true, class: 'audio-tag', type: 'audio/mpeg') 12 | = yield 13 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | 3 | config.cache_classes = false 4 | config.eager_load = false 5 | config.consider_all_requests_local = true 6 | 7 | config.action_controller.perform_caching = false 8 | config.action_mailer.raise_delivery_errors = false 9 | config.active_support.deprecation = :log 10 | config.active_record.migration_error = :page_load 11 | 12 | config.assets.debug = false 13 | config.assets.digest = true 14 | config.assets.raise_runtime_errors = true 15 | end 16 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/mp3.js: -------------------------------------------------------------------------------- 1 | $(document).on('ready, page:change',function(){ 2 | $('.header-circle').click(function(){ 3 | if($('.start-misic').offset().left == 0){ 4 | $('.audio-tag')[0].pause(); 5 | $('.stop-misic').hide(); 6 | $('.start-misic').show(); 7 | }else{ 8 | $('.audio-tag')[0].play(); 9 | $('.start-misic').hide(); 10 | $('.stop-misic').show(); 11 | } 12 | }) 13 | $('.audio-tag')[0].onended = function(){ 14 | $('.stop-misic').hide(); 15 | $('.start-misic').show(); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /app/views/welcome/rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! 2 | 3 | xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do 4 | xml.channel do 5 | 6 | xml.title 'Rina\'s Blog' 7 | xml.link 'http://liuzhem.me' 8 | xml.description 'hello world' 9 | 10 | @articles.each do |article| 11 | xml.item do 12 | xml.title article.title 13 | xml.link article_url(article) 14 | xml.description article.to_html.content 15 | xml.guid article_url(article) 16 | end 17 | end 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/admin/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::SessionsController < ApplicationController 2 | layout 'admin' 3 | 4 | def new 5 | end 6 | 7 | def create 8 | if params[:session][:username] == ENV['pusher_user'] && params[:session][:password] == ENV['pusher_pwd'] 9 | session[:login] = true 10 | redirect_to admin_articles_path 11 | else 12 | flash.now[:error] = '用户名或密码错误' 13 | render 'new' 14 | end 15 | end 16 | 17 | def destroy 18 | session[:login] = false 19 | flash[:notic] = '成功退出' 20 | redirect_to signin_path 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /app/views/admin/photos/index.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | a.pull-right.btn.btn-info.index-btn-info.self_photograph#upload-photo-album herf='#' 4 | |插入图片 5 | input.file-window-photo-album type='file' 6 | 7 | - @photos.each do |photo| 8 | .row 9 | .col-md-2 10 | = photo.image_name 11 | .col-md-3 12 | = image_tag photo.image_url, style: 'width: 50%' 13 | .col-md-2 14 | = photo.created_at.to_date 15 | .col-md-1 16 | = link_to "删除", admin_photo_path(photo), method: :delete, data: { confirm: "确认删除?" } 17 | 18 | = paginate @photos 19 | -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | http://liuzhen.me 11 | 12 | 13 | http://liuzhen.me/articles 14 | 15 | 16 | http://liuzhen.me/about 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/controllers/admin/photos_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::PhotosController < ApplicationController 2 | layout 'admin' 3 | 4 | def index 5 | @photos = Photo.photo_album.order(created_at: 'desc').page params[:page] 6 | end 7 | 8 | def upload 9 | @photo = Photo.new(image: params['Filedata'], photo_type: true) 10 | @photo.save! 11 | render json: {status: 'ok'} 12 | end 13 | 14 | def destroy 15 | @photo = Photo.find(params[:id]) 16 | if @photo.destroy 17 | flash[:success] = '删除成功' 18 | else 19 | flash[:error] = '删除失败' 20 | end 21 | redirect_to admin_photos_path 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | /public/uploads 19 | 20 | # Ignore application configuration 21 | /config/application.yml 22 | -------------------------------------------------------------------------------- /app/views/admin/articles/_form.html.slim: -------------------------------------------------------------------------------- 1 | = form_for [:admin, @article] do |f| 2 | = render 'shared/form_error_message' 3 | p.article-title 4 | = f.label '标题' 5 | = f.text_field :title 6 | .tabbable.tabs-left 7 | ul.nav.nav-tabs 8 | li.content-title.active 9 | = link_to '内容', '#' 10 | li.preview-title 11 | = link_to '预览', '#' 12 | a.pull-right#upload-photo herf='#' 13 | |插入图片 14 | input.file-window type='file' 15 | = f.text_area :content 16 | .markdown-body.markdown 17 | = f.submit '发表' 18 | .pull-right 19 | = link_to '排版说明', 'https://ruby-china.org/markdown', target: '_blank' 20 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | 3 | config.cache_classes = true 4 | 5 | config.eager_load = false 6 | 7 | config.serve_static_files = true 8 | config.static_cache_control = 'public, max-age=3600' 9 | 10 | config.consider_all_requests_local = true 11 | config.action_controller.perform_caching = false 12 | 13 | config.action_dispatch.show_exceptions = false 14 | 15 | config.action_controller.allow_forgery_protection = false 16 | 17 | config.action_mailer.delivery_method = :test 18 | 19 | config.active_support.test_order = :random 20 | 21 | config.active_support.deprecation = :stderr 22 | 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/comments_controller.rb: -------------------------------------------------------------------------------- 1 | class CommentsController < ApplicationController 2 | def index 3 | end 4 | 5 | def create 6 | @article = Article.find(params[:article_id]).to_html 7 | @comment = Comment.new(comment_params.merge(article_id: params[:article_id])) 8 | @comments = @article.comments.order(created_at: :desc) 9 | if !@comment.save 10 | flash[:error] = '添加评论失败' 11 | render 'articles/show' 12 | else 13 | flash[:notic] = '添加评论成功' 14 | redirect_to @article 15 | end 16 | end 17 | 18 | private 19 | def comment_params 20 | params.require(:comment).permit(:message, :username, :email) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/nigix.conf: -------------------------------------------------------------------------------- 1 | upstream rina { 2 | server unix:/tmp/unicorn_rina.sock fail_timeout=0; 3 | } 4 | 5 | server { 6 | listen 80; 7 | server_name liuzhen.me; 8 | root /home/ruby/rina/current/public; 9 | 10 | location ^~ /assets/ { 11 | gzip_static on; 12 | expires max; 13 | add_header Cache-Control public; 14 | } 15 | 16 | try_files $uri/index.html $uri @rina; 17 | location @rina { 18 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 | proxy_set_header Host $http_host; 20 | proxy_redirect off; 21 | proxy_pass http://rina; 22 | } 23 | 24 | error_page 500 502 503 504 /500.html; 25 | client_max_body_size 20M; 26 | keepalive_timeout 10; 27 | } 28 | -------------------------------------------------------------------------------- /app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | def index 3 | @articles = Article.all.order(created_at: :desc).limit(8) 4 | @meta_description = 'Rina\'s 个人博客, 分享生活, 工作与技术的相关文章' 5 | end 6 | 7 | def rss 8 | @articles = Article.all.order(created_at: :desc).limit(10) 9 | render :layout=>false 10 | response.headers["Content-Type"] = "application/xml; charset=utf-8" 11 | end 12 | 13 | def about 14 | @self_info = Article.self_info 15 | @meta_description = 'Rina\'s 个人信息, 关于工作经历及个人简介.' 16 | end 17 | 18 | def photo 19 | @photos = Photo.photo_album 20 | flash[:notic] = '主人还未上传相片' if @photos.blank? 21 | render :layout=>'photo' 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | config.cache_classes = true 3 | 4 | config.eager_load = true 5 | 6 | config.consider_all_requests_local = false 7 | config.action_controller.perform_caching = true 8 | 9 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 10 | 11 | config.assets.js_compressor = :uglifier 12 | 13 | config.assets.compile = false 14 | 15 | config.assets.digest = true 16 | 17 | config.log_level = :debug 18 | 19 | config.i18n.fallbacks = true 20 | 21 | config.active_support.deprecation = :notify 22 | 23 | config.log_formatter = ::Logger::Formatter.new 24 | 25 | config.active_record.dump_schema_after_migration = false 26 | end 27 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/unicorn/production.rb: -------------------------------------------------------------------------------- 1 | app_path = "/home/ruby/rina/current" 2 | 3 | worker_processes 1 4 | preload_app true 5 | timeout 180 6 | listen '/tmp/unicorn_rina.sock' 7 | user 'ruby', 'ruby' 8 | working_directory app_path 9 | pid "#{app_path}/tmp/pids/unicorn_rina.pid" 10 | stderr_path "log/unicorn.log" 11 | stdout_path "log/unicorn.log" 12 | 13 | before_fork do |server, worker| 14 | old_pid = "#{server.config[:pid]}.oldbin" 15 | if File.exists?(old_pid) && server.pid != old_pid 16 | begin 17 | Process.kill("QUIT", File.read(old_pid).to_i) 18 | rescue Errno::ENOENT, Errno::ESRCH 19 | # someone else did our job for us 20 | end 21 | end 22 | end 23 | 24 | after_fork do |server, worker| 25 | end 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | if ENV['RUBYTAOBAO'] 2 | source 'https://ruby.taobao.org' 3 | else 4 | source 'https://rubygems.org' 5 | end 6 | 7 | gem 'rails', '4.2.7.1' 8 | gem 'sqlite3' 9 | gem 'sass-rails', '~> 5.0' 10 | gem 'uglifier', '>= 1.3.0' 11 | gem 'coffee-rails', '~> 4.1.0' 12 | gem 'jquery-rails' 13 | gem 'turbolinks' 14 | gem 'jbuilder', '~> 2.0' 15 | gem 'bootstrap-sass', '~> 3.3.4' 16 | gem 'mina' 17 | gem 'unicorn' 18 | gem 'slim-rails' 19 | # 配置密码 20 | gem 'figaro' 21 | # markdown 工具 22 | gem 'redcarpet' 23 | gem 'rouge' 24 | # 保存图片 25 | gem 'carrierwave' 26 | # 图片截取 27 | gem "mini_magick" 28 | # 截断html 29 | gem 'truncate_html' 30 | # 分页 31 | gem 'kaminari' 32 | # 浏览器加载进度条 33 | gem 'nprogress-rails' 34 | 35 | group :development, :test do 36 | gem 'byebug' 37 | gem 'pry' 38 | gem 'web-console', '~> 2.3.0' 39 | gem 'spring' 40 | end 41 | 42 | -------------------------------------------------------------------------------- /app/models/article.rb: -------------------------------------------------------------------------------- 1 | require 'markdown' 2 | class Article < ActiveRecord::Base 3 | has_many :comments 4 | 5 | validates :title, :content, presence: true 6 | 7 | default_scope -> { where(self_info: false) } 8 | 9 | def self.self_info 10 | Article.unscoped.where(self_info: true).first || Article.create(title: '个人信息', content: '刘祯', self_info: true) 11 | end 12 | 13 | def short_title 14 | if title.length > 10 15 | title[0..10] + '...' 16 | else 17 | title 18 | end 19 | end 20 | 21 | def to_html 22 | self.content = content_html 23 | self 24 | end 25 | 26 | def content_html 27 | self.class.render_html(self.content) 28 | end 29 | 30 | def self.render_html(content) 31 | rd = CodeHTML.new 32 | md = Redcarpet::Markdown.new(rd, autolink: true, fenced_code_blocks: true) 33 | md.render(content) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | namespace :admin do 4 | resources :photos, only: [:index, :destroy] do 5 | collection do 6 | post :upload 7 | end 8 | end 9 | 10 | resources :articles do 11 | collection do 12 | post :preview 13 | post :photos 14 | end 15 | end 16 | end 17 | 18 | resources :demos 19 | 20 | resources :articles do 21 | collection do 22 | get :search 23 | post :search 24 | end 25 | end 26 | 27 | resources :comments 28 | 29 | get '/admin', to: 'admin/articles#index' 30 | 31 | post '/signin', to: 'admin/sessions#create' 32 | get '/signin', to: 'admin/sessions#new' 33 | get '/signout', to: 'admin/sessions#destroy' 34 | 35 | get '/blog/rss', to: 'welcome#rss' 36 | get '/about', to: 'welcome#about' 37 | get '/photo', to: 'welcome#photo' 38 | root 'welcome#index' 39 | end 40 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/admin/posts.scss: -------------------------------------------------------------------------------- 1 | body { 2 | .row { 3 | margin-bottom: 20px; 4 | } 5 | font-size: 17px; 6 | } 7 | 8 | .rina-admin-navbar{ 9 | min-height: 50px; 10 | height: 50px; 11 | 12 | a { 13 | padding-top: 15px; 14 | padding-bottom: 14px; 15 | line-height: 20px; 16 | } 17 | 18 | li > a { 19 | padding-top: 15px; 20 | padding-bottom: 14px; 21 | line-height: 20px; 22 | } 23 | } 24 | 25 | img{ 26 | display: block; 27 | } 28 | 29 | .rina-logo{ 30 | height: 50px; 31 | } 32 | 33 | .article-title{ 34 | margin-top: 60px; 35 | } 36 | 37 | #article_title{ 38 | width: 267px; 39 | margin-top: 26px; 40 | margin-bottom: 30px; 41 | } 42 | 43 | #article_content{ 44 | width: 1138px; 45 | height: 505px; 46 | } 47 | 48 | input[type='file']{ 49 | display: none; 50 | } 51 | 52 | .markdown-body{ 53 | display: none; 54 | border: 1px solid #C5C5C5; 55 | padding: 0px 10px; 56 | overflow: scroll; 57 | width: 1138px; 58 | height: 505px; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 00398415011cef6d1f66da3e36a28824fc9ccf935b8db980652d77e98ced94958b6f8e7a86a38fd6fb112370147dd791a95f35a69b70f9d0c345f5a4f3ffc7c2 15 | 16 | test: 17 | secret_key_base: 6472c9273338e4a4aeee8ef069affed6e54d55d85d4a99bfe2a6bb854eb204a983881ff5f776fdee48f393f942956bed60014930f13af73bf053c0185e22550c 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: 6472c9273338e4a4aeee8ef069affed6e54d55d85d4a99bfe2a6bb854eb204a983881ff5f776fdee48f393f942956bed60014930f13af73bf053c0185e22550c 23 | -------------------------------------------------------------------------------- /app/controllers/articles_controller.rb: -------------------------------------------------------------------------------- 1 | class ArticlesController < ApplicationController 2 | def index 3 | @articles = Article.all 4 | @meta_description = 'Rina\'s 所有博客文章.' 5 | end 6 | 7 | def show 8 | @article = Article.find(params[:id]).to_html 9 | @comment = Comment.new 10 | @comments = @article.comments.order(created_at: :desc) 11 | end 12 | 13 | def search 14 | params[:page] = 1 if params[:page].blank? 15 | @articles = Article.where("title like ? OR content like ?", "%#{params[:search]}%", "%#{params[:search]}%") 16 | @articles = @articles.page params[:page] 17 | if @articles.blank? 18 | flash.now[:error] = '没有你要找的内容' 19 | return 20 | end 21 | @articles.map do |a| 22 | a.title = a.title.gsub(params[:search].to_s, "#{params[:search]}") 23 | unless a.content.include?('/uploads/photo/image') 24 | a.content = a.content.gsub(params[:search].to_s, "#{params[:search]}") 25 | end 26 | htmlstring = TruncateHtml::HtmlString.new(a.to_html.content) 27 | a.content = TruncateHtml::HtmlTruncator.new(htmlstring).truncate 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module RinaBlog 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | 23 | # Do not swallow errors in after_commit/after_rollback callbacks. 24 | config.active_record.raise_in_transactional_callbacks = true 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/assets/javascripts/admin/posts.coffee: -------------------------------------------------------------------------------- 1 | $(document).on 'ready, page:change', -> 2 | 3 | $('.preview-title').click -> 4 | $('.preview-title').addClass('active') 5 | $('.content-title').removeClass('active') 6 | $('#article_content').hide() 7 | $('.markdown-body').show() 8 | $('.markdown-body').html('加载中...') 9 | $.post '/admin/articles/preview', { body: $('#article_content').val() }, (data)-> 10 | $('.markdown-body').html(data) 11 | false 12 | false 13 | 14 | $('.content-title').click -> 15 | $('.content-title').addClass('active') 16 | $('.preview-title').removeClass('active') 17 | $('.markdown-body').hide() 18 | $('#article_content').show() 19 | false 20 | 21 | $('#upload-photo').click -> 22 | $("input[type=file]").show().focus().click().hide() 23 | false 24 | 25 | opt = 26 | type: 'POST' 27 | url: "/admin/articles/photos" 28 | success: (data,status,xhr)-> 29 | txtBox = $("#article_content") 30 | caret_pos = txtBox.caret('pos') 31 | src_merged = "\n" + data + "\n" 32 | source = txtBox.val() 33 | before_text = source.slice(0, caret_pos) 34 | txtBox.val(before_text + src_merged + source.slice(caret_pos+1, source.count)) 35 | txtBox.caret('pos',caret_pos + src_merged.length) 36 | txtBox.focus() 37 | 38 | $('input.file-window').fileUpload(opt) 39 | -------------------------------------------------------------------------------- /app/views/layouts/admin.html.slim: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title 4 | | Rina's Blog 5 | meta name='viewport' content="width=device-width, initial-scale=1.0, max-scale=1.0" 6 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true 7 | = javascript_include_tag 'application', 'data-turbolinks-track' => true 8 | = csrf_meta_tags 9 | body 10 | nav.navbar.navbar-inverse.rina-admin-navbar 11 | .container-fluid 12 | .navbar-header 13 | button.navbar-toggle.collapsed type='button' data-toggle='collapse' data-target='#bs-example-navbar-collapse-1' 14 | span.sr-only 15 | | Toggle navigation 16 | span.icon-bar 17 | span.icon-bar 18 | span.icon-bar 19 | = link_to 'Rina', admin_articles_path, class: 'navbar-brand rina-logo' 20 | 21 | .collapse.navbar-collapse#bs-example-navbar-collapse-1 22 | ul.nav.navbar-nav 23 | li = link_to '创建博客', new_admin_article_path 24 | li = link_to '博客管理', admin_articles_path 25 | li = link_to '相册管理', admin_photos_path 26 | li = link_to '个人资料', edit_admin_article_path(Article.self_info) 27 | ul.nav.navbar-nav.navbar-right 28 | li= link_to '返回首页', '/' 29 | - if login? 30 | li = link_to '退出', '/signout' 31 | = yield 32 | -------------------------------------------------------------------------------- /app/assets/javascripts/photo.coffee: -------------------------------------------------------------------------------- 1 | $(document).on 'ready, page:change', -> 2 | 3 | $('.slider').tilesSlider({ random: true }) 4 | 5 | $slider = $('.slider-wrap') 6 | html = $slider.html() 7 | 8 | defaults = 9 | thumbSize: 20, 10 | onSlideshowEnd: -> $('.stop, .start').toggle() 11 | 12 | effects = 13 | 'default': { x:6, y:4, random: true }, 14 | simple: { x:6, y:4, effect: 'simple', random: true }, 15 | left: { x:1, y:8, effect: 'left' }, 16 | up: { x:20, y:1, effect: 'up', rewind: 60, backReverse: true }, 17 | leftright: { x:1, y:8, effect: 'leftright' }, 18 | updown: { x:20, y:1, effect: 'updown', cssSpeed: 500, backReverse: true }, 19 | switchlr: { x:20, y:1, effect: 'switchlr', backReverse: true }, 20 | switchud: { x:1, y:8, effect: 'switchud' }, 21 | fliplr: { x:20, y:1, effect: 'fliplr', backReverse: true }, 22 | flipud: { x:20, y:3, effect: 'flipud', reverse: true, rewind: 75 }, 23 | reduce: { x:6, y:4, effect: 'reduce' }, 24 | 360: { x:1, y:1, effect: '360', fade: true, cssSpeed: 600 } 25 | 26 | $('.start').click -> 27 | $(this).hide() 28 | $('.stop').show() 29 | $('.audio-tag')[0].play() 30 | $('.slider').tilesSlider('start') 31 | 32 | $('.stop').click -> 33 | $(this).hide() 34 | $('.audio-tag')[0].pause() 35 | $('.start').show() 36 | $('.slider').tilesSlider('stop') 37 | 38 | $('.slider').tilesSlider( $.extend( {}, defaults, effects['default'] ) ) 39 | -------------------------------------------------------------------------------- /app/controllers/admin/articles_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::ArticlesController < ApplicationController 2 | layout 'admin' 3 | 4 | before_action do 5 | unless login? 6 | flash[:error] = '请先登录后再操作' 7 | redirect_to signin_path 8 | end 9 | end 10 | 11 | def photos 12 | @photo = Photo.new(image: params["Filedata"]) 13 | @photo.save! 14 | render :text=> "![](#{@photo.image.url})" 15 | end 16 | 17 | def preview 18 | render plain: Article.render_html(params[:body] || "") 19 | end 20 | 21 | def index 22 | @articles = Article.all.order(created_at: 'desc').page params[:page] 23 | end 24 | 25 | def edit 26 | @article = Article.unscoped.find(params[:id]) 27 | end 28 | 29 | def update 30 | @article = Article.unscoped.find(params[:id]) 31 | @article.update(article_params) 32 | if @article.self_info 33 | redirect_to admin_articles_path 34 | else 35 | redirect_to @article.to_html 36 | end 37 | end 38 | 39 | def new 40 | @article = Article.new 41 | end 42 | 43 | def show 44 | end 45 | 46 | def create 47 | @article = Article.new(article_params) 48 | if @article.save 49 | redirect_to @article.to_html 50 | else 51 | flash.now[:error] = '新增博客失败' 52 | render :new 53 | end 54 | end 55 | 56 | def destroy 57 | @article = Article.find(params[:id]) 58 | @article.destroy 59 | redirect_to admin_articles_path 60 | end 61 | 62 | private 63 | def article_params 64 | params.require(:article).permit(:title, :content) 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /app/views/articles/show.html.slim: -------------------------------------------------------------------------------- 1 | h2.rina-title 2 | = @article.title 3 | .content.markdown 4 | == @article.content_html 5 | 6 | .create-comment 7 | = render 'shared/form_error_message' 8 | 9 | = form_for @comment, url: comments_path(article_id: @article.id) do |f| 10 | .form-group 11 | = f.text_area :message, rows: '3', class: 'form-control', placeholder: '留言...' 12 | .form-group 13 | = f.text_field :username, class: 'form-control form-comment', placeholder: '你的名字' 14 | .form-group 15 | = f.text_field :email, class: 'form-control form-comment', placeholder: '邮箱' 16 | .form-group 17 | = f.submit '提交', class: 'btn btn-default' 18 | 19 | .form-group 20 | - @comments.each_with_index do |comment, index| 21 | - if index == 0 22 | .comment-diag 23 | .comment-wrapper 24 | p.name 25 | = comment.username 26 | |• 27 | span.created-at 28 | = comment.created_at.strftime("%Y-%m-%d %H:%M") 29 | p.comment-content 30 | = comment.message 31 | 32 | .jiathis_style 33 | a.jiathis_button_tools_1 34 | a.jiathis_button_tools_2 35 | a.jiathis_button_tools_3 36 | a.jiathis_button_linkedin 37 | a.jiathis_button_tools_4 38 | a.jiathis_counter_style 39 | 40 | javascript: 41 | var jiathis_config = { 42 | appkey:{ 43 | "tpeople":"6045604360" 44 | }, 45 | "do_not_track":true, 46 | "ralateuid":{"tsina":"1647863564"}, 47 | "data_track_clickback":true 48 | }; 49 | 50 | script type="text/javascript" src="http://v3.jiathis.com/code/jia.js?uid=1" charset="utf-8" 51 | -------------------------------------------------------------------------------- /app/assets/javascripts/googleanalytics.coffee: -------------------------------------------------------------------------------- 1 | class @GoogleAnalytics 2 | 3 | @load: -> 4 | # Google Analytics depends on a global _gaq array. window is the global scope. 5 | window._gaq = [] 6 | window._gaq.push ["_setAccount", 'UA-69461736-1'] 7 | 8 | # Create a script element and insert it in the DOM 9 | ga = document.createElement("script") 10 | ga.type = "text/javascript" 11 | ga.async = true 12 | ga.src = ((if "https:" is document.location.protocol then "https://ssl" else "http://www")) + ".google-analytics.com/ga.js" 13 | firstScript = document.getElementsByTagName("script")[0] 14 | firstScript.parentNode.insertBefore ga, firstScript 15 | 16 | # If Turbolinks is supported, set up a callback to track pageviews on page:change. 17 | # If it isn't supported, just track the pageview now. 18 | if typeof Turbolinks isnt 'undefined' and Turbolinks.supported 19 | document.addEventListener "page:change", (-> 20 | GoogleAnalytics.trackPageview() 21 | ), true 22 | else 23 | GoogleAnalytics.trackPageview() 24 | 25 | @trackPageview: (url) -> 26 | unless GoogleAnalytics.isLocalRequest() 27 | if url 28 | window._gaq.push ["_trackPageview", url] 29 | else 30 | window._gaq.push ["_trackPageview"] 31 | window._gaq.push ["_trackPageLoadTime"] 32 | 33 | @isLocalRequest: -> 34 | GoogleAnalytics.documentDomainIncludes "local" 35 | 36 | @documentDomainIncludes: (str) -> 37 | document.domain.indexOf(str) isnt -1 38 | 39 | @analyticsId: -> 40 | # your google analytics ID(s) here... 41 | 42 | GoogleAnalytics.load() 43 | 44 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20160224042413) do 15 | 16 | create_table "articles", force: :cascade do |t| 17 | t.string "title" 18 | t.text "content" 19 | t.datetime "created_at", null: false 20 | t.datetime "updated_at", null: false 21 | t.boolean "self_info", default: false 22 | end 23 | 24 | create_table "comments", force: :cascade do |t| 25 | t.string "username" 26 | t.string "email" 27 | t.string "message" 28 | t.datetime "created_at", null: false 29 | t.datetime "updated_at", null: false 30 | t.integer "article_id" 31 | end 32 | 33 | create_table "photos", force: :cascade do |t| 34 | t.string "image" 35 | t.datetime "created_at", null: false 36 | t.datetime "updated_at", null: false 37 | t.boolean "photo_type", default: false 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /app/uploaders/image_uploader.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class ImageUploader < CarrierWave::Uploader::Base 4 | 5 | # Include RMagick or MiniMagick support: 6 | # include CarrierWave::RMagick 7 | include CarrierWave::MiniMagick 8 | 9 | # Choose what kind of storage to use for this uploader: 10 | storage :file 11 | # storage :fog 12 | 13 | # Override the directory where uploaded files will be stored. 14 | # This is a sensible default for uploaders that are meant to be mounted: 15 | def store_dir 16 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" 17 | end 18 | 19 | # Provide a default URL as a default if there hasn't been a file uploaded: 20 | # def default_url 21 | # # For Rails 3.1+ asset pipeline compatibility: 22 | # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_')) 23 | # 24 | # "/images/fallback/" + [version_name, "default.png"].compact.join('_') 25 | # end 26 | 27 | # Process files as they are uploaded: 28 | # process :scale => [200, 300] 29 | # 30 | # def scale(width, height) 31 | # # do something 32 | # end 33 | 34 | # Create different versions of your uploaded files: 35 | 36 | process :resize_to_fit => [1000, 600] 37 | 38 | # version :thumb do 39 | # process :resize_to_fit => [50, 50] 40 | # end 41 | 42 | # Add a white list of extensions which are allowed to be uploaded. 43 | # For images you might use something like this: 44 | # def extension_white_list 45 | # %w(jpg jpeg gif png) 46 | # end 47 | 48 | # Override the filename of the uploaded files: 49 | # Avoid using model.id or version_name here, see uploader/store.rb for details. 50 | # def filename 51 | # "something.jpg" if original_filename 52 | # end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rina's blog 2 | 3 | ## 1.0 博客已经废弃,最新版本 v2.0 已上线 4 | 5 | 详情请查看: 6 | 7 | [![Rina's GitHub stats](https://github-readme-stats.vercel.app/api?username=liuzhenangel)](https://github.com/anuraghazra/github-readme-stats) 8 | 9 | ![Rina's GitHub stats](https://github-readme-stats.vercel.app/api?username=liuzhenangel&show_icons=true&hide_border=true&hide_rank=true&hide_title=true&card_width=120) 10 | 11 | ## 1.0 Demo 12 | ![](https://l.ruby-china.org/photo/2017/6617885b-e153-41fa-8269-10622074b34d.png!large) 13 | ![](https://l.ruby-china.org/photo/2017/b568b5b2-0090-4228-be0a-3684e4e3d465.png!large) 14 | ![](https://l.ruby-china.org/photo/2017/c2b7cc17-f397-47cd-b1ee-438736bfa85a.png!large) 15 | ![](https://l.ruby-china.org/photo/2017/b2279479-1b8e-4a5c-a0f8-0ce6560494f1.png!large) 16 | ![](https://l.ruby-china.org/photo/2017/ad5640de-77a9-4b54-9c39-42eb7c393783.png!large) 17 | ![](https://l.ruby-china.org/photo/2017/246418eb-6682-454a-87b0-c4b30eb15070.png!large) 18 | ![](https://l.ruby-china.org/photo/2017/5996e2f3-8b16-4545-8a9b-66ae7d28a7d4.png!large) 19 | ![](https://l.ruby-china.org/photo/2017/14ca8aae-8f45-425a-94e8-639114b7983d.png!large) 20 | 21 | 22 | 23 | ## 项目介绍 24 | 25 | Rina_Blog 项目是一个 Rails 项目, 前端框架用的 bootstrap, 主要功能有: 后台管理, 订阅, 相册, Markdown 在线编辑预览, 个人简历在线编辑, 评论等功能. 26 | 27 | ## 核心技术框架 28 | 29 | * Rails 30 | * bootstrap 31 | * turbolinks 32 | * mina 33 | * carrierwave 34 | * kaminari 35 | 36 | ## 系统依赖 37 | 38 | * Rails 4.2.1 39 | 40 | ## 开发环境准备 41 | 42 | 第一步, 安装项目依赖 43 | 44 | ```bash 45 | $ bundle install 46 | ``` 47 | 48 | 49 | 第二步, 启动服务 50 | 51 | ```bash 52 | $ rails s 53 | ``` 54 | 55 | 第三步, 浏览器访问: 56 | 57 | 结束. 58 | 59 | ## 如何发布? 60 | 61 | 第一步, 配置nginx 62 | 63 | 先根据项目里的 `/config/deploy.rb` `/config/unicorn/production.rb` `/config/nigix.conf` 文件, 修改其中的配置, 然后将 `/config/nigix.conf` 文件复制到你的服务器上 nginx 所在目录的 `/etc/nginx/conf.d` 目录下, 命名为 xxx.conf 的文件. 然后重启 nginx. 64 | 65 | 66 | 第二步, 在服务器上初始化 67 | 68 | ```bash 69 | $ mina init 70 | ``` 71 | 72 | 第三步, 发布 73 | 74 | ```bash 75 | $ mina deploy 76 | ``` 77 | 78 | ## 学习参考资料 79 | 80 | Ruby on Rails 实践: 81 | 82 | Ruby on Rails 指南: 83 | 84 | ## 贡献者 85 | 86 | * Rina 87 | 88 | ## LICENSE 89 | 90 | MIT 91 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.slim: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title 4 | - if content_for?(:title) 5 | = yield(:title) 6 | - else 7 | |Rina's Blog 8 | 9 | meta name='description' content="#{@meta_description}" 10 | meta name='viewport' content="width=device-width, initial-scale=1.0, max-scale=1.0" 11 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true 12 | = javascript_include_tag 'application', 'data-turbolinks-track' => true 13 | = javascript_include_tag 'mp3' 14 | = csrf_meta_tags 15 | body 16 | .container 17 | .row.rina-header 18 | .col-sm-4 19 | .ih-item.circle.effect15.left_to_right.header-circle 20 | = link_to '#' do 21 | .img 22 | = image_tag('7.jpg') 23 | .info 24 | h3 Music box 25 | p.start-misic Click to Start 26 | p.stop-misic style='display:none' Click to Stop 27 | = audio_tag("sound.mp3", loop: true, class: 'audio-tag', type: 'audio/mpeg') 28 | = link_to 'https://github.com/liuzhenangel/Rina_Blog', target: '_blank' do 29 | = image_tag('fork_me_on_github.png', class: 'fork_me_on_github') 30 | .col-sm-8.index-navbar-right 31 | =link_to '订阅', '/blog/rss', class: 'btn btn-info index-btn-info a' 32 | =link_to '主页', '/', class: 'btn btn-info index-btn-info a' 33 | =link_to '所有', articles_path, class: 'btn btn-info index-btn-info a' 34 | =link_to '相册', '/photo', class: 'btn btn-info index-btn-info a' 35 | //=link_to 'Demo', demos_path, class: 'btn btn-info index-btn-info a' 36 | =link_to '关于', '/about', class: 'btn btn-info index-btn-info a' 37 | .row 38 | .col-md-9 39 | = yield 40 | .col-md-3 41 | .search-field 42 | = form_for(:search, url: search_articles_path, method: 'get') do |f| 43 | = text_field_tag 'search', params[:search], class: 'form-control', placeholder: 'Search' 44 | ul.nav.rina-new-article 45 | li 46 | h3.widgettitle 最新文章 47 | - @newest_articles.each do |article| 48 | li.rina-new-title 49 | = link_to article.short_title, article_path(article) 50 | .page-footer 51 | span.link liuzhen.me 52 | span.time © 2015 - 2015 53 | .license 54 | span 55 | = link_to 'Rina\'s Blog', 'https://github.com/liuzhenangel/Rina_Blog', target: '_blank' 56 | -------------------------------------------------------------------------------- /config/deploy.rb: -------------------------------------------------------------------------------- 1 | require 'mina/bundler' 2 | require 'mina/rails' 3 | require 'mina/git' 4 | # require 'mina/rbenv' # for rbenv support. (http://rbenv.org) 5 | require 'mina/rvm' # for rvm support. (http://rvm.io) 6 | 7 | set :domain, 'yafeilee.me' 8 | set :deploy_to, '/home/ruby/rina' 9 | set :repository, 'git@github.com:liuzhenangel/Rina_Blog.git' 10 | set :branch, 'master' 11 | set :app_path, "#{deploy_to}/#{current_path}" 12 | 13 | set :shared_paths, ['config/database.yml', 'config/application.yml', 'log', 'db/production.sqlite3', 'tmp', 'public/uploads'] 14 | 15 | set :user, 'ruby' # Username in the server to SSH to. 16 | set :port, '22' # SSH port number. 17 | set :forward_agent, true # SSH forward_agent. 18 | 19 | # This task is the environment that is loaded for most commands, such as 20 | # `mina deploy` or `mina rake`. 21 | task :environment do 22 | # If you're using rbenv, use this to load the rbenv environment. 23 | # Be sure to commit your .rbenv-version to your repository. 24 | # invoke :'rbenv:load' 25 | 26 | invoke :'rvm:use[2.3.1]' 27 | #queue! %[source /usr/local/rvm/scripts/rvm] 28 | #queue! %[rvm use 2.0.0] 29 | # For those using RVM, use this to load an RVM version@gemset. 30 | end 31 | 32 | # Put any custom mkdir's in here for when `mina setup` is ran. 33 | # For Rails apps, we'll make some of the shared paths that are shared between 34 | # all releases. 35 | task :setup => :environment do 36 | queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"] 37 | queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/log"] 38 | 39 | queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"] 40 | queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/config"] 41 | 42 | queue! %[touch "#{deploy_to}/#{shared_path}/config/database.yml"] 43 | queue %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/database.yml'."] 44 | end 45 | 46 | desc "Deploys the current version to the server." 47 | task :deploy => :environment do 48 | deploy do 49 | # Put things that will set up an empty directory into a fully set-up 50 | # instance of your project. 51 | invoke :'git:clone' 52 | invoke :'deploy:link_shared_paths' 53 | invoke :'bundle:install' 54 | invoke :'rails:db_migrate' 55 | invoke :'rails:assets_precompile' 56 | invoke :'deploy:cleanup' 57 | 58 | to :launch do 59 | invoke :'unicorn:restart' 60 | end 61 | end 62 | end 63 | 64 | namespace :unicorn do 65 | set :unicorn_pid, "#{app_path}/tmp/pids/unicorn_rina.pid" 66 | set :start_unicorn, %{ 67 | cd #{app_path} && bundle exec unicorn -c config/unicorn/#{rails_env}.rb -E #{rails_env} -D 68 | } 69 | 70 | # Start task 71 | # ------------------------------------------------------------------------------ 72 | desc "Start unicorn" 73 | task :start => :environment do 74 | queue 'echo "-----> Start Unicorn"' 75 | queue! start_unicorn 76 | end 77 | 78 | # Stop task 79 | # ------------------------------------------------------------------------------ 80 | desc "Stop unicorn" 81 | task :stop do 82 | queue 'echo "-----> Stop Unicorn"' 83 | queue! %{ 84 | mkdir -p "#{app_path}/tmp/pids" 85 | test -s "#{unicorn_pid}" && kill -QUIT `cat "#{unicorn_pid}"` && rm -rf "#{unicorn_pid}" && echo "Stop Ok" && exit 0 86 | echo >&2 "Not running" 87 | } 88 | end 89 | # Restart task 90 | # ------------------------------------------------------------------------------ 91 | desc "Restart unicorn using 'upgrade'" 92 | task :restart => :environment do 93 | invoke 'unicorn:stop' 94 | invoke 'unicorn:start' 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /app/assets/stylesheets/highlight.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight .c { color: #B0B2B0; font-style: italic } /* Comment */ 3 | .highlight .err { } /* Error */ 4 | .highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */ 5 | .highlight .o { color: #666666 } /* Operator */ 6 | .highlight .cm { color: #B0B2B0; font-style: italic } /* Comment.Multiline */ 7 | .highlight .cp { color: #B0B2B0 } /* Comment.Preproc */ 8 | .highlight .c1 { color: #B0B2B0; font-style: italic } /* Comment.Single */ 9 | .highlight .cs { color: #B0B2B0; font-weight: bold } /* Comment.Special */ 10 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 11 | .highlight .ge { font-style: italic } /* Generic.Emph */ 12 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 13 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 14 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 15 | .highlight .go { color: #808080 } /* Generic.Output */ 16 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 17 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 18 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 19 | .highlight .gt { color: #0040D0 } /* Generic.Traceback */ 20 | .highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ 21 | .highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ 22 | .highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */ 23 | .highlight .kp { color: #AA22FF } /* Keyword.Pseudo */ 24 | .highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ 25 | .highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */ 26 | .highlight .m { color: #666666 } /* Literal.Number */ 27 | .highlight .s { color: #BB4444 } /* Literal.String */ 28 | .highlight .na { color: #BB4444 } /* Name.Attribute */ 29 | .highlight .nb { color: #AA22FF } /* Name.Builtin */ 30 | .highlight .nc { color: #0000FF } /* Name.Class */ 31 | .highlight .no { color: #880000 } /* Name.Constant */ 32 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 33 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 34 | .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 35 | .highlight .nf { color: #00A000 } /* Name.Function */ 36 | .highlight .nl { color: #A0A000 } /* Name.Label */ 37 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 38 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 39 | .highlight .nv { color: #B8860B } /* Name.Variable */ 40 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 41 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 43 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 44 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 45 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 46 | .highlight .sb { color: #BB4444 } /* Literal.String.Backtick */ 47 | .highlight .sc { color: #BB4444 } /* Literal.String.Char */ 48 | .highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ 49 | .highlight .s2 { color: #BB4444 } /* Literal.String.Double */ 50 | .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 51 | .highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */ 52 | .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 53 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 54 | .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ 55 | .highlight .s1 { color: #BB4444 } /* Literal.String.Single */ 56 | .highlight .ss { color: #B8860B } /* Literal.String.Symbol */ 57 | .highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ 58 | .highlight .vc { color: #B8860B } /* Name.Variable.Class */ 59 | .highlight .vg { color: #B8860B } /* Name.Variable.Global */ 60 | .highlight .vi { color: #B8860B } /* Name.Variable.Instance */ 61 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ 62 | .highlight {font-size: 16px !important} 63 | -------------------------------------------------------------------------------- /app/assets/stylesheets/markdown.scss: -------------------------------------------------------------------------------- 1 | .markdown { 2 | position:relative; 3 | line-height: 1.8em; font-size:14px; text-overflow: ellipsis; word-wrap: break-word; 4 | font-family: "RobotoDraft", "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif !important; 5 | img { max-width: 100%; } 6 | p, 7 | pre, 8 | ul, 9 | ol, 10 | blockquote { margin-bottom: 16px; } 11 | 12 | p { font-size: 14px; line-height: 1.5em; } 13 | hr { border:2px dashed $gray; border-bottom:0px; margin: 18px auto; width:50%; } 14 | blockquote { 15 | margin: 0 18px 15px 18px; 16 | padding: 0; 17 | padding-left: 32px; 18 | border: 0px; 19 | quotes: "\201C""\201D""\2018""\2019"; 20 | position: relative; 21 | line-height: 1.45; 22 | p { display:inline; font-size:12px; color: #999; } 23 | &:before, 24 | &:after { 25 | display: block; 26 | content: "\201C"; 27 | font-size: 35px; 28 | position: absolute; 29 | font-family: serif; 30 | left: 0px; 31 | top: 0px; 32 | color: #aaa; 33 | } 34 | } 35 | pre { 36 | font-family: Menlo, Monaco, "Courier New", monospace; 37 | font-size: 12px; 38 | background-color: #F5F5F5; 39 | border: 0px; 40 | padding: 5px; 41 | color: #444; 42 | overflow: auto; 43 | border-radius: 0px; 44 | code { 45 | display: block; 46 | line-height: 150%; 47 | padding: 0!important; 48 | font-size: 12px!important; 49 | background-color: #F5F5F5 !important; 50 | border: none!important; } 51 | } 52 | p:last-child, 53 | blockquote:last-child, 54 | pre:last-child { margin-bottom:0; } 55 | pre::-webkit-scrollbar { 56 | height: 8px; 57 | width: 8px; } 58 | 59 | pre::-webkit-scrollbar-thumb:horizontal { 60 | width: 25px; 61 | background-color: #ccc; 62 | -webkit-border-radius: 4px; } 63 | 64 | pre::-webkit-scrollbar-track-piece { 65 | margin-bottom: 10px; 66 | background-color: #e5e5e5; 67 | border-bottom-left-radius: 4px 4px; 68 | border-bottom-right-radius: 4px 4px; 69 | border-top-left-radius: 4px 4px; 70 | border-top-right-radius: 4px 4px; } 71 | 72 | pre::-webkit-scrollbar-thumb:vertical { 73 | height: 25px; 74 | background-color: #ccc; 75 | -webkit-border-radius: 4px; 76 | -webkit-box-shadow: 0 1px 1px white; } 77 | 78 | code { 79 | font-size: 12px!important; 80 | background-color: #F5F5F5 !important; 81 | color: #444 !important; 82 | padding: 1px 2px !important; 83 | border: 0px; 84 | margin: 2px; 85 | border-radius: 0px; 86 | word-break: break-all; 87 | line-height: 20px; 88 | font-family:Monaco,Menlo, "Courier New", monospace; 89 | } 90 | a:link, 91 | a:visited { 92 | color:#0069D6 !important; text-decoration: none !important; 93 | } 94 | a:hover { text-decoration: underline !important; color:#00438A !important; } 95 | a.mention-floor { color:#60b566 !important; margin-right: 3px; } 96 | a.mention { 97 | color:#777 !important; font-weight: bold; 98 | margin-right: 2px; 99 | b { color:#777 !important; font-weight: normal; } 100 | } 101 | h1, 102 | h2, 103 | h3, 104 | h4, 105 | h5, 106 | h6 { 107 | font-weight:bold; text-align:left; 108 | margin-top: 10px !important; margin-bottom: 16px; 109 | } 110 | h1 { font-size: 26px !important; text-align: center; margin-bottom: 30px !important; } 111 | h2, 112 | h3, 113 | h4 { 114 | text-align: left; 115 | font-weight: bold; 116 | font-size: 16px !important; 117 | line-height: 100%; 118 | margin: 0; color: #555; 119 | margin-top: 16px; margin-bottom:16px; 120 | border-bottom:1px solid #eee; 121 | padding-bottom: 5px; 122 | } 123 | h2 { font-size: 20px !important; border-bottom-width: 2px; padding-bottom: 15px; margin-top: 20px; margin-bottom:20px; color: #111; } 124 | h3 { font-size: 18px !important; padding-bottom: 10px; margin-top: 20px; margin-bottom: 20px; color: #333; } 125 | h5, h6 { font-size: 15px; line-height: 100%; color: #777; } 126 | h6 { font-size: 14px; color: #999; } 127 | 128 | strong { color:#000; } 129 | ul, 130 | ol { 131 | list-style-position: inside; 132 | list-style-type: square; 133 | margin:0; 134 | margin-bottom: 20px; 135 | padding:0px 20px; 136 | p, 137 | blockquote, 138 | pre { margin-bottom:8px; } 139 | li { line-height:1.6em; padding:2px 0; color:#333; font-size:13px; } 140 | ul { list-style-type: circle; margin-bottom: 0px; } 141 | } 142 | ol { 143 | list-style-type: decimal; 144 | ol { list-style-type: lower-alpha; margin-bottom: 0px; } 145 | } 146 | img { vertical-align: top; max-width: 100%; } 147 | a.zoom-image { cursor: zoom-in; } 148 | a.at_floor { color: #60B566 !important; } 149 | a.at_user { color: #0069D6 !important; } 150 | } 151 | -------------------------------------------------------------------------------- /app/assets/stylesheets/welcome.scss: -------------------------------------------------------------------------------- 1 | .fork_me_on_github { 2 | @media (max-width: 780px) { 3 | display: none; 4 | } 5 | z-index: 10001; 6 | position: fixed; 7 | top: 0; 8 | right: 0; 9 | border: 0; 10 | width: auto; 11 | } 12 | .navbar-brand img{ 13 | width: 65px; 14 | } 15 | 16 | //相册 17 | .photo-body{ 18 | width: 960px; 19 | margin: 0px auto; 20 | background: image-url('bg.png'); 21 | overflow-y: scroll; 22 | } 23 | 24 | .photo-container{ 25 | width: 990px; 26 | } 27 | 28 | section{ 29 | @media (max-width: 780px) { 30 | margin-top: 0px; 31 | } 32 | margin-top: 50px; 33 | } 34 | 35 | a { 36 | color: #088BBF; 37 | text-decoration: none; 38 | } 39 | h1 { 40 | display: inline-block; 41 | margin: 0; 42 | } 43 | #down { 44 | position: relative; 45 | top: -18px; 46 | margin: 0 1em; 47 | } 48 | .slider-wrap { 49 | background: white; 50 | padding: 10px; 51 | width: 960px; 52 | height: 447px; 53 | box-shadow: 0 1px 4px rgba(0,0,0,.2); 54 | } 55 | .slider { 56 | width: 940px; 57 | height: 400px; 58 | } 59 | .slider strong { color: #F0E10E; } 60 | article { 61 | position: relative; 62 | } 63 | .title { 64 | padding: 1em 0; 65 | position: relative; 66 | margin-bottom: 25px; 67 | } 68 | .code, 69 | #effects-select { 70 | display: inline-block; 71 | } 72 | #effects-select { 73 | padding: .4em; 74 | margin-right: 1em; 75 | font-size: 14px; 76 | border: 1px solid #aaa; 77 | } 78 | label { margin-right: .5em; } 79 | code { 80 | padding: .5em 1em; 81 | background: rgba(255,255,255,.75); 82 | font-size: 14px; 83 | margin: 0 .2em; 84 | font-family: Ubuntu Mono, monospace; 85 | } 86 | .action { 87 | position: absolute; 88 | right: 0; 89 | top: 13px; 90 | color: white; 91 | background: black; 92 | border: 0; 93 | width: 80px; 94 | padding: .6em 0; 95 | } 96 | .stop { 97 | display: none; 98 | background: #088BBF; 99 | text-shadow: 1px 1px rgba(0,0,0,.4) 100 | } 101 | 102 | footer { 103 | margin-top: 2em; 104 | } 105 | 106 | //end 相册 107 | 108 | .about-background{ 109 | background-image: image-url("CZ16VX4.gif"); 110 | } 111 | 112 | p{ 113 | margin: 18px 0 10px; 114 | } 115 | 116 | .square-p{ 117 | margin: 0 0 10px; 118 | } 119 | 120 | .header-circle{ 121 | @media (max-width: 780px) { 122 | margin: auto; 123 | } 124 | margin-top: 10px; 125 | margin-bottom: 10px; 126 | } 127 | 128 | .self-info{ 129 | padding-left: 20px; 130 | margin-top: 26px; 131 | margin-bottom: 30px; 132 | } 133 | 134 | .worker-use{ 135 | padding-left: 35px; 136 | } 137 | 138 | .title-self{ 139 | font-weight: bolder; 140 | } 141 | 142 | .page-footer{ 143 | border-top: 1px solid #dddddd; 144 | padding: 1rem 0 2rem; 145 | text-align: right; 146 | } 147 | 148 | .page-footer .link { 149 | margin-right: 1rem; 150 | } 151 | 152 | .page-footer .license { 153 | margin-top: 1rem; 154 | } 155 | 156 | .index-row{ 157 | @media (max-width: 780px) { 158 | margin: auto; 159 | margin-top: 5px; 160 | } 161 | margin-top: 10px; 162 | margin-bottom: 15px; 163 | } 164 | 165 | .index-btn-info{ 166 | @media (max-width: 780px) { 167 | margin-left: 2px; 168 | } 169 | margin-left: 15px; 170 | } 171 | 172 | .index-navbar-right{ 173 | @media (max-width: 780px) { 174 | margin-top: 17px; 175 | text-align: left; 176 | padding-right: 0; 177 | } 178 | 179 | margin-top: 98px; 180 | text-align: right; 181 | padding-right: 64px; 182 | } 183 | 184 | .a{ 185 | color: #FFFFFF; 186 | } 187 | 188 | .rina-new-article{ 189 | margin-top: 20px; 190 | } 191 | 192 | .all-article{ 193 | @media (max-width: 780px) { 194 | margin-left: 0px; 195 | } 196 | margin-top: 20px; 197 | margin-left: 205px; 198 | } 199 | 200 | .create-comment{ 201 | margin-top: 20px; 202 | } 203 | 204 | .comment-wrapper { 205 | margin-bottom: 0.5rem; 206 | } 207 | 208 | .comment-wrapper .comment-content { 209 | word-wrap: break-word; 210 | color: #000000; 211 | white-space: pre-wrap; 212 | word-break: break-all; 213 | } 214 | 215 | .comment-diag .created-at { 216 | color: #b3b3b3; 217 | } 218 | 219 | .comment-wrapper .name { 220 | color: #000000; 221 | } 222 | 223 | .comment-content { 224 | padding-bottom: 2.275rem; 225 | padding-left: 0.275rem; 226 | border-bottom: 1px dashed #8a8a8a; 227 | margin-bottom: 0; 228 | white-space: pre; 229 | } 230 | 231 | .comment-diag { 232 | color: #EBEBEB; 233 | border-top: 1px solid #5E5E5E; 234 | padding-top: 1rem; 235 | } 236 | 237 | .form-comment{ 238 | width: 40%; 239 | } 240 | 241 | .widgettitle{ 242 | font-size: 15px; 243 | margin-bottom: 1px; 244 | padding: .3em 5px 1em 0em; 245 | border-bottom: 1px solid #ccc; 246 | } 247 | 248 | .rina-title{ 249 | padding: .5rem 0; 250 | text-align: center; 251 | } 252 | 253 | .article-show{ 254 | text-align: center; 255 | } 256 | 257 | .content.markdown{ 258 | padding: 1.2rem 0; 259 | font-size: 16px; 260 | } 261 | 262 | .search-field { 263 | margin-top: 30px; 264 | } 265 | 266 | .search-field input{ 267 | width: 13em; 268 | margin: 0; 269 | padding: 6px 6px 6px 3px; 270 | border: 1px solid #ccc; 271 | background: #fff image-url('search.gif') no-repeat 236px center; 272 | font-size: 1.2em; 273 | } 274 | 275 | .rina-new-title{ 276 | margin: 0; 277 | padding: .3em 5px .4em 1em; 278 | line-height: .3; 279 | border-bottom: 1px dotted #bbb; 280 | font-size: .9em; 281 | } 282 | -------------------------------------------------------------------------------- /public/jia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 如何自定义分享图ssdfasdf 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 27 |
28 |
29 |
30 |
如何自定义分享图片? 35 |
36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 |
49 |

50 | 使用说明: 网站主可以自己定义要分享的图片,其自定义标准代码如下: 51 |

52 |
<!-- JiaThis Button BEGIN -->
 53 |                                         <script "text/javascript">
 54 |                                         var jiathis_config = {
 55 |                                           pic:”自定义分享的图片地址”
 56 |                                           }
 57 |                                           </script>
 58 |                                           <script src="http://v2.jiathis.com/code/jiathis_r.js?move=0"></script>
 59 |                                           <!-- JiaThis Button END -->
 60 |           
61 |

62 | 63 |

64 |

65 |   66 |

67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | 81 |
82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 | 90 | 101 |
102 | 103 | 104 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionmailer (4.2.7.1) 5 | actionpack (= 4.2.7.1) 6 | actionview (= 4.2.7.1) 7 | activejob (= 4.2.7.1) 8 | mail (~> 2.5, >= 2.5.4) 9 | rails-dom-testing (~> 1.0, >= 1.0.5) 10 | actionpack (4.2.7.1) 11 | actionview (= 4.2.7.1) 12 | activesupport (= 4.2.7.1) 13 | rack (~> 1.6.2) 14 | rack-test (~> 0.6.2) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | rails-html-sanitizer (~> 1.0.4) 17 | actionview (4.2.7.1) 18 | activesupport (= 4.2.7.1) 19 | builder (~> 3.1) 20 | erubis (~> 2.7.0) 21 | rails-dom-testing (~> 1.0, >= 1.0.5) 22 | rails-html-sanitizer (~> 1.0.4) 23 | activejob (4.2.7.1) 24 | activesupport (= 4.2.7.1) 25 | globalid (>= 0.3.0) 26 | activemodel (4.2.7.1) 27 | activesupport (= 4.2.7.1) 28 | builder (~> 3.1) 29 | activerecord (4.2.7.1) 30 | activemodel (= 4.2.7.1) 31 | activesupport (= 4.2.7.1) 32 | arel (~> 6.0) 33 | activesupport (4.2.7.1) 34 | i18n (~> 0.7) 35 | json (~> 1.7, >= 1.7.7) 36 | minitest (~> 5.1) 37 | thread_safe (~> 0.3, >= 0.3.4) 38 | tzinfo (~> 1.1) 39 | arel (6.0.4) 40 | autoprefixer-rails (8.6.3) 41 | execjs 42 | binding_of_caller (0.8.0) 43 | debug_inspector (>= 0.0.1) 44 | bootstrap-sass (3.3.7) 45 | autoprefixer-rails (>= 5.2.1) 46 | sass (>= 3.3.4) 47 | builder (3.2.3) 48 | byebug (10.0.2) 49 | carrierwave (1.2.2) 50 | activemodel (>= 4.0.0) 51 | activesupport (>= 4.0.0) 52 | mime-types (>= 1.16) 53 | coderay (1.1.2) 54 | coffee-rails (4.1.1) 55 | coffee-script (>= 2.2.0) 56 | railties (>= 4.0.0, < 5.1.x) 57 | coffee-script (2.4.1) 58 | coffee-script-source 59 | execjs 60 | coffee-script-source (1.12.2) 61 | concurrent-ruby (1.0.5) 62 | crass (1.0.4) 63 | debug_inspector (0.0.3) 64 | erubis (2.7.0) 65 | execjs (2.7.0) 66 | ffi (1.9.25) 67 | figaro (1.1.1) 68 | thor (~> 0.14) 69 | globalid (0.4.1) 70 | activesupport (>= 4.2.0) 71 | i18n (0.9.5) 72 | concurrent-ruby (~> 1.0) 73 | jbuilder (2.7.0) 74 | activesupport (>= 4.2.0) 75 | multi_json (>= 1.2) 76 | jquery-rails (4.3.3) 77 | rails-dom-testing (>= 1, < 3) 78 | railties (>= 4.2.0) 79 | thor (>= 0.14, < 2.0) 80 | json (1.8.6) 81 | kaminari (1.1.1) 82 | activesupport (>= 4.1.0) 83 | kaminari-actionview (= 1.1.1) 84 | kaminari-activerecord (= 1.1.1) 85 | kaminari-core (= 1.1.1) 86 | kaminari-actionview (1.1.1) 87 | actionview 88 | kaminari-core (= 1.1.1) 89 | kaminari-activerecord (1.1.1) 90 | activerecord 91 | kaminari-core (= 1.1.1) 92 | kaminari-core (1.1.1) 93 | kgio (2.11.2) 94 | loofah (2.2.2) 95 | crass (~> 1.0.2) 96 | nokogiri (>= 1.8.2) 97 | mail (2.7.0) 98 | mini_mime (>= 0.1.1) 99 | method_source (0.9.0) 100 | mime-types (3.1) 101 | mime-types-data (~> 3.2015) 102 | mime-types-data (3.2016.0521) 103 | mina (1.2.3) 104 | open4 (~> 1.3.4) 105 | rake 106 | mini_magick (4.8.0) 107 | mini_mime (1.0.0) 108 | mini_portile2 (2.3.0) 109 | minitest (5.11.3) 110 | multi_json (1.13.1) 111 | nokogiri (1.8.3) 112 | mini_portile2 (~> 2.3.0) 113 | nprogress-rails (0.2.0.2) 114 | open4 (1.3.4) 115 | pry (0.11.3) 116 | coderay (~> 1.1.0) 117 | method_source (~> 0.9.0) 118 | rack (1.6.10) 119 | rack-test (0.6.3) 120 | rack (>= 1.6.2) 121 | rails (4.2.7.1) 122 | actionmailer (= 4.2.7.1) 123 | actionpack (= 4.2.7.1) 124 | actionview (= 4.2.7.1) 125 | activejob (= 4.2.7.1) 126 | activemodel (= 4.2.7.1) 127 | activerecord (= 4.2.7.1) 128 | activesupport (= 4.2.7.1) 129 | bundler (>= 1.3.0, < 2.0) 130 | railties (= 4.2.7.1) 131 | sprockets-rails 132 | rails-deprecated_sanitizer (1.0.3) 133 | activesupport (>= 4.2.0.alpha) 134 | rails-dom-testing (1.0.9) 135 | activesupport (>= 4.2.0, < 5.0) 136 | nokogiri (~> 1.8.2) 137 | rails-deprecated_sanitizer (>= 1.0.1) 138 | rails-html-sanitizer (1.0.4) 139 | loofah (~> 2.2.1, >= 2.2.2) 140 | railties (4.2.7.1) 141 | actionpack (= 4.2.7.1) 142 | activesupport (= 4.2.7.1) 143 | rake (>= 0.8.7) 144 | thor (>= 0.18.1, < 2.0) 145 | raindrops (0.19.0) 146 | rake (12.3.1) 147 | rb-fsevent (0.10.3) 148 | rb-inotify (0.9.10) 149 | ffi (>= 0.5.0, < 2) 150 | redcarpet (3.4.0) 151 | rouge (3.1.1) 152 | sass (3.5.6) 153 | sass-listen (~> 4.0.0) 154 | sass-listen (4.0.0) 155 | rb-fsevent (~> 0.9, >= 0.9.4) 156 | rb-inotify (~> 0.9, >= 0.9.7) 157 | sass-rails (5.0.7) 158 | railties (>= 4.0.0, < 6) 159 | sass (~> 3.1) 160 | sprockets (>= 2.12.5, < 4.0) 161 | sprockets-rails (>= 2.12.5, < 4.0) 162 | tilt (>= 1.1, < 3) 163 | slim (3.0.9) 164 | temple (>= 0.7.6, < 0.9) 165 | tilt (>= 1.3.3, < 2.1) 166 | slim-rails (3.1.3) 167 | actionpack (>= 3.1) 168 | railties (>= 3.1) 169 | slim (~> 3.0) 170 | spring (2.0.2) 171 | activesupport (>= 4.2) 172 | sprockets (3.7.2) 173 | concurrent-ruby (~> 1.0) 174 | rack (> 1.6.2, < 3) 175 | sprockets-rails (3.2.1) 176 | actionpack (>= 4.0) 177 | activesupport (>= 4.0) 178 | sprockets (>= 3.0.0) 179 | sqlite3 (1.3.13) 180 | temple (0.8.0) 181 | thor (0.20.0) 182 | thread_safe (0.3.6) 183 | tilt (2.0.8) 184 | truncate_html (0.9.3) 185 | turbolinks (5.1.1) 186 | turbolinks-source (~> 5.1) 187 | turbolinks-source (5.1.0) 188 | tzinfo (1.2.5) 189 | thread_safe (~> 0.1) 190 | uglifier (4.1.12) 191 | execjs (>= 0.3.0, < 3) 192 | unicorn (5.4.0) 193 | kgio (~> 2.6) 194 | raindrops (~> 0.7) 195 | web-console (2.3.0) 196 | activemodel (>= 4.0) 197 | binding_of_caller (>= 0.7.2) 198 | railties (>= 4.0) 199 | sprockets-rails (>= 2.12.5, < 4.0) 200 | 201 | PLATFORMS 202 | ruby 203 | 204 | DEPENDENCIES 205 | bootstrap-sass (~> 3.3.4) 206 | byebug 207 | carrierwave 208 | coffee-rails (~> 4.1.0) 209 | figaro 210 | jbuilder (~> 2.0) 211 | jquery-rails 212 | kaminari 213 | mina 214 | mini_magick 215 | nprogress-rails 216 | pry 217 | rails (= 4.2.7.1) 218 | redcarpet 219 | rouge 220 | sass-rails (~> 5.0) 221 | slim-rails 222 | spring 223 | sqlite3 224 | truncate_html 225 | turbolinks 226 | uglifier (>= 1.3.0) 227 | unicorn 228 | web-console (~> 2.3.0) 229 | 230 | BUNDLED WITH 231 | 1.15.4 232 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/jquery.tiles.min.css: -------------------------------------------------------------------------------- 1 | .tiles-preload .tiles-slider-wrap *{-webkit-transition:none!important;-moz-transition:none!important;-ms-transition:none!important;-o-transition:none!important;transition:none!important}.tiles-slider-wrap *{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}.tiles-slider-wrap a:focus{outline:0}.tiles-slider-wrap,.tiles-tile{position:relative}.tiles-wrap{position:absolute;height:100%;width:100%;-webkit-perspective:1000px;-moz-perspective:1000px;-ms-perspective:1000px;-o-perspective:1000px;perspective:1000px}.tiles-tile{float:left;top:0;left:0;right:0;bottom:0;opacity:1;z-index:0;-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.tiles-timer{position:absolute;top:0;height:5px;width:0;background:#088bbf}.tiles-next,.tiles-prev{opacity:0;position:absolute;z-index:1;top:50%;margin:0 .5em;margin-top:-24px;text-indent:-9999px;width:48px;height:48px;line-height:50px;background:url(../img/prev_next.png) left no-repeat;color:white;-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.tiles-next{right:0;background-position:right}.tiles-slider-wrap:hover .tiles-next,.tiles-slider-wrap:hover .tiles-prev{opacity:1}.tiles-nav:hover ~ .tiles-next,.tiles-nav:hover ~ .tiles-prev{opacity:0}.tiles-nav{position:absolute;top:100%;left:50%;margin-top:1em}.tiles-nav .tiles-nav-item{position:relative;-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.tiles-nav .tiles-nav-item.tiles-nav-active{cursor:default}.tiles-nav .tiles-nav-item:not(.tiles-nav-active):hover span{visibility:visible;opacity:1;margin-top:-1em}.tiles-nav .tiles-pagination{text-decoration:none;color:#222;padding:.1em .5em;margin:0 .1em;border-radius:2px}.tiles-nav .tiles-pagination:hover{text-decoration:none;color:#088bbf}.tiles-nav .tiles-pagination.tiles-nav-active{background:#088bbf;color:#fff}.tiles-nav .tiles-bullet{display:inline-block;width:16px;height:16px;margin:0 .3em;border-radius:16px;background:#ddd}.tiles-nav .tiles-bullet.tiles-nav-active{background:#088bbf}.tiles-nav span{position:absolute;z-index:1;visibility:hidden;opacity:0;padding:.3em;margin-top:-1.5em;background:rgba(255,255,255,0.85);box-shadow:0 0 5px rgba(0,0,0,0.3);border-radius:2px;border-top:1px solid white;-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out;-webkit-backface-visibility:hidden}.tiles-nav span:after,.tiles-nav span:before{content:"";position:absolute;top:100%;left:50%;margin-left:-10px;border-style:solid;border-width:10px;border-color:transparent;border-top-color:rgba(255,255,255,0.85)}.tiles-nav span:before{margin-top:1px;border-top-color:rgba(0,0,0,0.5)}.tiles-nav img{display:block}.tiles-description{position:absolute;z-index:1;bottom:0;left:0;width:100%;padding:1em 2em;line-height:1.5;visibility:hidden;opacity:0;color:white;background:rgba(0,0,0,0.65);box-shadow:0 -1px 2px rgba(0,0,0,0.3);-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.tiles-description p{position:relative;padding:0;margin:0;left:-5em;-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.tiles-description-active{opacity:1;visibility:visible}.tiles-description-active p{left:0}.tiles-description-empty{display:none}.tiles-slider-s100 .tiles-tile{-webkit-transition:all .1s ease-in-out;-moz-transition:all .1s ease-in-out;-ms-transition:all .1s ease-in-out;-o-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.tiles-slider-s200 .tiles-tile{-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-ms-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.tiles-slider-s300 .tiles-tile{-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;-ms-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.tiles-slider-s400 .tiles-tile{-webkit-transition:all .4s ease-in-out;-moz-transition:all .4s ease-in-out;-ms-transition:all .4s ease-in-out;-o-transition:all .4s ease-in-out;transition:all .4s ease-in-out}.tiles-slider-s500 .tiles-tile{-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out}.tiles-slider-s600 .tiles-tile{-webkit-transition:all .6s ease-in-out;-moz-transition:all .6s ease-in-out;-ms-transition:all .6s ease-in-out;-o-transition:all .6s ease-in-out;transition:all .6s ease-in-out}.tiles-slider-s700 .tiles-tile{-webkit-transition:all .7s ease-in-out;-moz-transition:all .7s ease-in-out;-ms-transition:all .7s ease-in-out;-o-transition:all .7s ease-in-out;transition:all .7s ease-in-out}.tiles-slider-s800 .tiles-tile{-webkit-transition:all .8s ease-in-out;-moz-transition:all .8s ease-in-out;-ms-transition:all .8s ease-in-out;-o-transition:all .8s ease-in-out;transition:all .8s ease-in-out}.tiles-slider-s900 .tiles-tile{-webkit-transition:all .9s ease-in-out;-moz-transition:all .9s ease-in-out;-ms-transition:all .9s ease-in-out;-o-transition:all .9s ease-in-out;transition:all .9s ease-in-out}.tiles-slider-s1000 .tiles-tile{-webkit-transition:all 1s ease-in-out;-moz-transition:all 1s ease-in-out;-ms-transition:all 1s ease-in-out;-o-transition:all 1s ease-in-out;transition:all 1s ease-in-out}.tiles-tile.tiles-reset{-webkit-transition:all 0s ease-in-out;-moz-transition:all 0s ease-in-out;-ms-transition:all 0s ease-in-out;-o-transition:all 0s ease-in-out;transition:all 0s ease-in-out}.tiles-simple-anim{opacity:0}.tiles-default-anim{position:relative;opacity:0;box-shadow:0 0 10px black;top:-5%;left:5%;z-index:1}.tiles-y1.tiles-up-anim{height:1px!important}.tiles-x1.tiles-left-normal{clear:both}.tiles-x1.tiles-left-anim{width:0!important}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x2{left:50%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x4{left:25%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x6{left:16.666666666666668%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x8{left:12.5%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x10{left:10%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x12{left:8.333333333333334%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x14{left:7.142857142857143%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x16{left:6.25%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x18{left:5.555555555555555%}.tiles-switchlr-anim.tiles-even.tiles-y1.tiles-x20{left:5%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x2{left:-50%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x4{left:-25%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x6{left:-16.666666666666668%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x8{left:-12.5%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x10{left:-10%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x12{left:-8.333333333333334%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x14{left:-7.142857142857143%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x16{left:-6.25%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x18{left:-5.555555555555555%}.tiles-switchlr-anim.tiles-odd.tiles-y1.tiles-x20{left:-5%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y2{top:50%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y4{top:25%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y6{top:16.666666666666668%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y8{top:12.5%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y10{top:10%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y12{top:8.333333333333334%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y14{top:7.142857142857143%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y16{top:6.25%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y18{top:5.555555555555555%}.tiles-switchud-anim.tiles-even.tiles-x1.tiles-y20{top:5%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y2{top:-50%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y4{top:-25%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y6{top:-16.666666666666668%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y8{top:-12.5%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y10{top:-10%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y12{top:-8.333333333333334%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y14{top:-7.142857142857143%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y16{top:-6.25%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y18{top:-5.555555555555555%}.tiles-switchud-anim.tiles-odd.tiles-x1.tiles-y20{top:-5%}.tiles-fliplr-anim{-webkit-transform:rotateY(180deg);-moz-transform:rotateY(180deg);-ms-transform:rotateY(180deg);-o-transform:rotateY(180deg);transform:rotateY(180deg)}.tiles-flipud-anim{-webkit-transform:rotateX(180deg);-moz-transform:rotateX(180deg);-ms-transform:rotateX(180deg);-o-transform:rotateX(180deg);transform:rotateX(180deg)}.tiles-reduce-anim{-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);transform:scale(0)}.tiles-360-anim{-webkit-transform:rotateZ(360deg) scale(0);-moz-transform:rotateZ(360deg) scale(0);-ms-transform:rotateZ(360deg) scale(0);-o-transform:rotateZ(360deg) scale(0);transform:rotateZ(360deg) scale(0)}.tiles-leftright-anim.tiles-even{-webkit-transform:translate(50%,0);-moz-transform:translate(50%,0);-ms-transform:translate(50%,0);-o-transform:translate(50%,0);transform:translate(50%,0)}.tiles-leftright-anim.tiles-odd{-webkit-transform:translate(-50%,0);-moz-transform:translate(-50%,0);-ms-transform:translate(-50%,0);-o-transform:translate(-50%,0);transform:translate(-50%,0)}.tiles-updown-anim.tiles-even{-webkit-transform:translate(0,50%);-moz-transform:translate(0,50%);-ms-transform:translate(0,50%);-o-transform:translate(0,50%);transform:translate(0,50%)}.tiles-updown-anim.tiles-odd{-webkit-transform:translate(0,-50%);-moz-transform:translate(0,-50%);-ms-transform:translate(0,-50%);-o-transform:translate(0,-50%);transform:translate(0,-50%)}.tiles-slider-wrap .tiles-switchlr-anim,.tiles-slider-wrap .tiles-switchud-anim,.tiles-slider-wrap .tiles-fliplr-anim,.tiles-slider-wrap .tiles-y1.tiles-up-anim,.tiles-slider-wrap .tiles-x1.tiles-left-anim,.tiles-slider-wrap .tiles-updown-anim,.tiles-slider-wrap .tiles-leftright-anim,.tiles-slider-wrap .tiles-flipud-anim,.tiles-slider-wrap .tiles-reduce-anim{opacity:0;visibility:hidden}.tiles-slider-leftright .tiles-wrap,.tiles-slider-updown .tiles-wrap,.tiles-slider-360 .tiles-wrap{overflow:hidden} -------------------------------------------------------------------------------- /vendor/assets/javascripts/jquery.tiles.js: -------------------------------------------------------------------------------- 1 | ;(function( $, window, undefined ) { 2 | 3 | // Globals: 4 | 5 | var _defaults = { 6 | x: 4, y: 4, 7 | slider: false, 8 | effect: 'default', 9 | fade: false, 10 | random: false, 11 | reverse: false, 12 | backReverse: false, 13 | limit: false, 14 | rewind: false, 15 | auto: false, 16 | loop: false, 17 | slideSpeed: 3500, 18 | tileSpeed: 800, 19 | cssSpeed: 300, 20 | nav: true, 21 | bullets: true, 22 | navWrap: null, 23 | thumbs: true, 24 | thumbSize: 25, 25 | timer: true, 26 | beforeChange: $.noop, 27 | afterChange: $.noop, 28 | onSlideshowEnd: $.noop 29 | } 30 | , Utils = { 31 | // http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript 32 | shuffle: function(o) { 33 | for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); 34 | return o; 35 | }, 36 | range: function(min, max, rand) { 37 | var out = []; 38 | for (var i=min; i<=max; i++) out.push(i); 39 | if (rand) return Utils.shuffle(out); 40 | return out; 41 | } 42 | } 43 | 44 | // Constructor: 45 | 46 | function TilesSlider( element, options ) { 47 | 48 | this.opts = $.extend( {}, _defaults, options ) 49 | this.klass = 'tiles-'+ this.opts.effect 50 | this.klassNormal = this.klass + '-normal' 51 | this.klassAnim = this.klass + '-anim' 52 | this.n_tiles = this.opts.x * this.opts.y 53 | 54 | this.$container = $( element ) 55 | this.$images = this.$container.find('img') 56 | 57 | this.imgWidth = this.$container.width() 58 | this.imgHeight = this.$container.height() 59 | 60 | this.timeouts = [] 61 | this.slideshow = null 62 | this.isAnimating = null 63 | 64 | // Assign in _init when elements are generated 65 | this.$navLinks = null 66 | this.$descriptions = null 67 | this.$timer = null 68 | 69 | if ( this.opts.rewind ) { this.opts.fade = true } 70 | 71 | this._init() 72 | 73 | } 74 | 75 | // Methods: 76 | 77 | TilesSlider.prototype = { 78 | 79 | _init: function() { 80 | 81 | var self = this 82 | , o = self.opts 83 | 84 | // Do nothing if there are no images 85 | if ( !self.$images.length ) { return false } 86 | 87 | self.$container.addClass('tiles-slider-wrap tiles-slider-'+ 88 | o.effect +' tiles-slider-s'+ o.cssSpeed) 89 | 90 | // Generate tiles 91 | self.$images.each(function() { self._generateTiles( $(this) ) }) 92 | 93 | // Remove from DOM to handle in slider 94 | self.$wraps = self.$container.find('.tiles-wrap').detach() 95 | self.$wraps.first().addClass('tiles-wrap-current').appendTo( self.$container ) 96 | 97 | if ( o.nav ) { 98 | self._addNav() 99 | self.$navLinks = self.$container.find('.tiles-nav-item') 100 | } 101 | 102 | if ( o.timer ) { 103 | self._addTimer() 104 | self.$timer = self.$container.find('.tiles-timer') 105 | } 106 | 107 | self._setupDescriptions() 108 | self.$descriptions = self.$container.find('.tiles-description') 109 | self._showOrHideDescription() 110 | 111 | // Prevent css3 transitions on load 112 | $('body').addClass('tiles-preload') 113 | $( window ).load(function(){ $('body').removeClass('tiles-preload') }) 114 | 115 | if ( o.auto ) { self.start() } 116 | 117 | }, 118 | 119 | _addTimer: function() { 120 | this.$container.append('
') 121 | }, 122 | 123 | _updateTimer: function() { 124 | if ( this.opts.timer ) { 125 | this.$timer.animate( { width: '100%' }, this.opts.slideSpeed, 'linear' ) 126 | } 127 | }, 128 | 129 | _resetTimer: function() { 130 | if ( this.opts.timer ) { 131 | this.$timer.stop( true ).width( 0 ) 132 | } 133 | }, 134 | 135 | _addNav: function() { 136 | 137 | var self = this 138 | , o = self.opts 139 | // double-wrap in case a string is passed 140 | , $nav = $( o.navWrap || '
' ).addClass('tiles-nav') 141 | , links = [], $links, thumb 142 | , $next = $('Next »') 143 | , $prev = $('« Prev') 144 | , thumbHeight = self.imgHeight * o.thumbSize / 100 145 | , thumbWidth = self.imgWidth * o.thumbSize / 100 146 | 147 | for ( var i = 1; i < self.$wraps.length + 1; i++ ) { 148 | thumb = '' 149 | links.push(''+ 150 | ( !o.bullets ? i : '') + ( o.thumbs ? thumb : '' ) + '') 151 | } 152 | 153 | $links = $( links.join('') ) 154 | 155 | // Events 156 | $links.click(function() { 157 | var $this = $(this) 158 | , idx = $links.index( $this ) 159 | self._navigate( idx, $.noop ) 160 | }) 161 | $links.first().addClass('tiles-nav-active') // init 162 | 163 | $next.click(function() { self.next() }) 164 | $prev.click(function() { self.prev() }) 165 | 166 | $links.add( $next ).add( $prev ) 167 | .click(function(e) { e.preventDefault() }) 168 | 169 | // Insert in DOM 170 | 171 | if ( o.navWrap ) { 172 | $nav.append( $links ) 173 | } else { 174 | self.$container.append( $nav.append( $links ) ) 175 | // Adjust center 176 | $nav.css( 'margin-left', '-'+ $nav.outerWidth()/2 +'px' ) 177 | } 178 | 179 | self.$container.append( $prev, $next ) 180 | 181 | // Adjust thumbnails when already in DOM 182 | $links.find('img').height( thumbHeight ).width( thumbWidth ) 183 | $links.find('span').each(function(){ 184 | var $this = $(this) 185 | $this.css({ 186 | top: -$this.outerHeight() - $nav.outerHeight(), 187 | left: -( $this.outerWidth() - $this.parent().outerWidth() ) / 2 188 | }) 189 | }) 190 | 191 | }, 192 | 193 | _updateNav: function() { 194 | if ( this.opts.nav ) { 195 | this.$navLinks.removeClass('tiles-nav-active') 196 | .eq( this._getCurrentIdx() ).addClass('tiles-nav-active') 197 | } 198 | }, 199 | 200 | _setupDescriptions: function() { 201 | // Add empty description if it doesn't exist 202 | // so we don't mess with the index 203 | this.$images.each(function() { 204 | if ( !$(this).next('p').length ) { $(this).after('

') } 205 | }) 206 | this.$container.find('p').each(function() { 207 | $(this).wrapAll('

') 209 | }) 210 | }, 211 | 212 | _showOrHideDescription: function( toggle ) { 213 | this.$descriptions.removeClass('tiles-description-active') 214 | .eq( this._getCurrentIdx() ).toggleClass( 'tiles-description-active', toggle ) 215 | }, 216 | 217 | _generateTiles: function( $img ) { 218 | 219 | var self = this 220 | , o = self.opts 221 | , tiles = [], $tiles 222 | 223 | for ( var i = 0; i < self.n_tiles; i++ ) { 224 | tiles.push('
') 225 | } 226 | 227 | $tiles = $( tiles.join('') ) 228 | 229 | $tiles.addClass('tiles-x'+ o.x +' tiles-y'+ o.y) 230 | .filter(':odd').addClass('tiles-odd').end() 231 | .filter(':even').addClass('tiles-even') 232 | 233 | $tiles.css({ 234 | width: self.imgWidth / o.x, 235 | height: self.imgHeight / o.y, 236 | backgroundImage: 'url('+ $img.attr('src') +')' 237 | }) 238 | 239 | // Insert in DOM 240 | $('
').append( $tiles ).insertAfter( $img.hide() ) 241 | 242 | // Calculate offset when image is in DOM 243 | $tiles.each(function(){ 244 | var $this = $(this), pos = $this.position() 245 | $this.css('backgroundPosition', -pos.left +'px '+ -pos.top +'px') 246 | }) 247 | 248 | }, 249 | 250 | _animateTiles: function( $wrap, callback ) { 251 | 252 | callback = callback || $.noop 253 | 254 | var self = this 255 | , o = self.opts 256 | , range = Utils.range( 0, self.n_tiles, o.random ) 257 | , delay = Math.floor( o.tileSpeed / self.n_tiles ) 258 | , limit = range.length / ( 100/o.limit ) 259 | , done = self.n_tiles > 1 ? o.tileSpeed + o.cssSpeed : o.cssSpeed 260 | , $tiles = $wrap.find('.tiles-tile') 261 | 262 | self.isAnimating = true 263 | 264 | if ( o.reverse ) { range = range.reverse() } 265 | 266 | if ( o.limit ) { 267 | o.reverse 268 | ? range.splice( 0, limit ) 269 | : range.splice( limit, range.length ) 270 | } 271 | 272 | $.each( range, function( i, tile ) { 273 | 274 | var theTile = $tiles.eq( tile ) 275 | , theDelay = i * delay 276 | 277 | setTimeout(function() { theTile.addClass( self.klassAnim ) }, theDelay ) 278 | 279 | if ( o.rewind ) { 280 | theDelay += o.cssSpeed / ( 100/o.rewind ) 281 | setTimeout(function() { theTile.removeClass( self.klassAnim ) }, theDelay ) 282 | } 283 | 284 | }) 285 | 286 | // Callback 287 | setTimeout(function() { 288 | self.isAnimating = false 289 | callback() 290 | }, done ) 291 | 292 | }, 293 | 294 | _resetTiles: function( $wrap ) { 295 | $wrap.find('.tiles-tile').removeClass( this.klassAnim ) 296 | }, 297 | 298 | _getCurrentWrap: function() { 299 | return this.$wraps.filter('.tiles-wrap-current') 300 | }, 301 | 302 | _getCurrentIdx: function() { 303 | return this.$wraps.index( this._getCurrentWrap() ) 304 | }, 305 | 306 | _navigate: function( idx, callback ) { 307 | 308 | var self = this 309 | , o = self.opts 310 | , $cur = self._getCurrentWrap() 311 | , $target = self.$wraps.eq( idx ) 312 | , curIdx = self._getCurrentIdx() 313 | 314 | if ( idx === curIdx || self.isAnimating ) { 315 | return false 316 | } 317 | 318 | self.$container.find('.tiles-wrap').removeClass('tiles-wrap-current') 319 | $target.addClass('tiles-wrap-current').prependTo( self.$container ) 320 | 321 | if ( o.fade ) { 322 | $target.fadeOut(1).fadeIn( o.tileSpeed ) 323 | } 324 | 325 | if ( o.rewind ) { 326 | $cur.fadeIn(1).delay( o.tileSpeed/2 ).fadeOut( o.tileSpeed ) 327 | } 328 | 329 | if ( o.backReverse ) { 330 | o.reverse = idx < curIdx 331 | } 332 | 333 | self._animateTiles( $cur, function() { 334 | $cur.remove() 335 | self._resetTiles( $cur ) 336 | self._showOrHideDescription( true ) 337 | if ( self.slideshow ) { self.stop().start() } 338 | if ( callback ) { callback() } 339 | o.afterChange() 340 | }) 341 | 342 | o.beforeChange() 343 | 344 | self._updateNav() 345 | self._resetTimer() 346 | self._showOrHideDescription( false ) 347 | 348 | return this 349 | 350 | }, 351 | 352 | // Public methods: 353 | 354 | prev: function( callback ) { 355 | 356 | var self = this 357 | , $cur = self._getCurrentWrap() 358 | , isFirst = $cur.is( self.$wraps.first() ) 359 | , idx = isFirst ? self.$wraps.length - 1 : self._getCurrentIdx() - 1 360 | 361 | return this._navigate( idx, callback || $.noop ) 362 | 363 | }, 364 | 365 | next: function( callback ) { 366 | 367 | var self = this 368 | , $cur = self._getCurrentWrap() 369 | , isLast = $cur.is( self.$wraps.last() ) 370 | , idx = isLast ? 0 : self._getCurrentIdx() + 1 371 | 372 | return this._navigate( idx, callback || $.noop ) 373 | 374 | }, 375 | 376 | start: function() { 377 | 378 | var self = this 379 | , o = self.opts 380 | , totalSpeed = o.slideSpeed + o.cssSpeed 381 | , endLoop = totalSpeed * ( self.$wraps.length - self._getCurrentIdx() ) 382 | 383 | if ( self.n_tiles > 1 ) { endLoop += o.tileSpeed } 384 | 385 | this.slideshow = true 386 | self.timeouts.push( setTimeout(function(){ self.next() }, o.slideSpeed ) ) 387 | 388 | if ( !o.loop ) { 389 | self.timeouts.push( setTimeout(function(){ 390 | self.stop() 391 | o.onSlideshowEnd() 392 | }, endLoop ) ); 393 | } 394 | 395 | this._updateTimer() 396 | 397 | return this 398 | 399 | }, 400 | 401 | stop: function() { 402 | $.each( this.timeouts, function( i, v ) { clearTimeout( v ) }) 403 | this.slideshow = false 404 | this._resetTimer() 405 | return this 406 | } 407 | 408 | } 409 | 410 | // jQuery plugin: 411 | 412 | $.fn.tilesSlider = function( options ) { 413 | var args = Array.prototype.slice.call( arguments, 1 ) 414 | return this.each(function() { 415 | if ( typeof options === 'string' ) { 416 | var plugin = $.data( this, 'tiles-slider' ) 417 | plugin[ options ].apply( plugin, args ) 418 | } else if ( !$.data(this, 'tiles-slider') ) { 419 | $.data( this, 'tiles-slider', new TilesSlider( this, options ) ) 420 | } 421 | }) 422 | } 423 | 424 | }( jQuery, window )) 425 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/jquery.html5-fileupload.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery HTML5 File Upload 3 | * 4 | * Author: timdream at gmail.com 5 | * Web: http://timc.idv.tw/html5-file-upload/ 6 | * 7 | * Ajax File Upload that use real xhr, 8 | * built with getAsBinary, sendAsBinary, FormData, FileReader, ArrayBuffer, BlobBuilder and etc. 9 | * works in Firefox 3, Chrome 5, Safari 5 and higher 10 | * 11 | * Image resizing and uploading currently works in Fx 3 and up, and Chrome 9 (dev) and up only. 12 | * Extra settings will allow current Webkit users to upload the original image 13 | * or send the resized image in base64 form. 14 | * 15 | * Usage: 16 | * $.fileUploadSupported // a boolean value indicates if the browser is supported. 17 | * $.imageUploadSupported // a boolean value indicates if the browser could resize image and upload in binary form. 18 | * $.fileUploadAsBase64Supported // a boolean value indicate if the browser upload files in based64. 19 | * $.imageUploadAsBase64Supported // a boolean value indicate if the browser could resize image and upload in based64. 20 | * $('input[type=file]').fileUpload(ajaxSettings); //Make a input[type=file] select-and-send file upload widget 21 | * $('#any-element').fileUpload(ajaxSettings); //Make a element receive dropped file 22 | * //TBD $('form#fileupload').fileUpload(ajaxSettings); //Send a ajax form with file 23 | * //TBD $('canvas').fileUpload(ajaxSettings); //Upload given canvas as if it's an png image. 24 | * 25 | * ajaxSettings is the object contains $.ajax settings that will be passed to. 26 | * Available extended settings are: 27 | * fileType: 28 | * regexp check against filename extension; You should always checked it again on server-side. 29 | * e.g. /^(gif|jpe?g|png|tiff?)$/i for images 30 | * fileMaxSize: 31 | * Maxium file size allowed in bytes. Use scientific notation for converience. 32 | * e.g. 1E4 for 1KB, 1E8 for 1MB, 1E9 for 10MB. 33 | * If you really care the difference between 1024 and 1000, use Math.pow(2, 10) 34 | * fileError(info, textStatus, textDescription): 35 | * callback function when there is any error preventing file upload to start, 36 | * $.ajax and ajax events won't be called when error. 37 | * Use $.noop to overwrite default alert function. 38 | * imageMaxWidth, imageMaxHeight: 39 | * Use any of the two settings to enable client-size image resizing. 40 | * Image will be resized to fit into given rectangle. 41 | * File size and type limit checking will be ignored. 42 | * allowUploadOriginalImage: 43 | * Set to true if you accept original image to be uploaded as a fallback 44 | * when image resizing functionality is not availible (such as Webkit browsers). 45 | * File size and type limit will be enforced. 46 | * allowDataInBase64: 47 | * Alternatively, you may wish to resize the image anyway and send the data 48 | * in base64. The data will be 133% larger and you will need to process it further with 49 | * server-side script. 50 | * This setting might work with browsers which could read file but cannot send it in original 51 | * binary (no known browser are designed this way though) 52 | * forceResize: 53 | * Set to true will cause the image being re-sampled even if the resized image 54 | * has the same demension as the original one. 55 | * imageType: 56 | * Acceptable values are: 'jpeg', 'png', or 'auto'. 57 | * 58 | * TBD: 59 | * ability to change settings after binding (you can unbind and bind again as a workaround) 60 | * multipole file handling 61 | * form intergation 62 | * 63 | */ 64 | 65 | (function($) { 66 | // Don't do logging if window.log function does not exist. 67 | var log = window.log || $.noop; 68 | 69 | // jQuery.ajax config 70 | var config = { 71 | fileError: function (info, textStatus, textDescription) { 72 | window.alert(textDescription); 73 | } 74 | }; 75 | 76 | // Feature detection 77 | 78 | // Read as binary string: FileReader API || Gecko-specific function (Fx3) 79 | var canReadAsBinaryString = (window.FileReader || window.File.prototype.getAsBinary); 80 | // Read file using FormData interface 81 | var canReadFormData = !!(window.FormData); 82 | // Read file into data: URL: FileReader API || Gecko-specific function (Fx3) 83 | var canReadAsBase64 = (window.FileReader || window.File.prototype.getAsDataURL); 84 | 85 | var canResizeImageToBase64 = !!(document.createElement('canvas').toDataURL); 86 | var canResizeImageToBinaryString = canResizeImageToBase64 && window.atob; 87 | var canResizeImageToFile = !!(document.createElement('canvas').mozGetAsFile); 88 | 89 | // Send file in multipart/form-data with binary xhr (Gecko-specific function) 90 | // || xhr.send(blob) that sends blob made with ArrayBuffer. 91 | var canSendBinaryString = ( 92 | (window.XMLHttpRequest && window.XMLHttpRequest.prototype.sendAsBinary) 93 | || (window.ArrayBuffer && window.BlobBuilder) 94 | ); 95 | // Send file as in FormData object 96 | var canSendFormData = !!(window.FormData); 97 | // Send image base64 data by extracting data: URL 98 | var canSendImageInBase64 = !!(document.createElement('canvas').toDataURL); 99 | 100 | var isSupported = ( 101 | (canReadAsBinaryString && canSendBinaryString) 102 | || (canReadFormData && canSendFormData) 103 | ); 104 | var isImageSupported = ( 105 | canReadAsBase64 && ( 106 | (canResizeImageToBinaryString && canSendBinaryString) 107 | || (canResizeImageToFile && canSendFormData) 108 | ) 109 | ); 110 | var isSupportedInBase64 = canReadAsBase64; 111 | var isImageSupportedInBase64 = canReadAsBase64 && canResizeImageToBase64; 112 | 113 | var dataURLtoBase64 = function (dataurl) { 114 | return dataurl.substring(dataurl.indexOf(',')+1, dataurl.length); 115 | } 116 | 117 | // Step 1: check file info and attempt to read the file 118 | // paramaters: Ajax settings, File object 119 | var handleFile = function (settings, file) { 120 | var info = { 121 | // properties of standard File object || Gecko 1.9 properties 122 | type: file.type || '', // MIME type 123 | size: file.size || file.fileSize, 124 | name: file.name || file.fileName 125 | }; 126 | 127 | settings.resizeImage = !!(settings.imageMaxWidth || settings.imageMaxHeight); 128 | 129 | if (settings.resizeImage && !isImageSupported && settings.allowUploadOriginalImage) { 130 | log('WARN: Fall back to upload original un-resized image.'); 131 | settings.resizeImage = false; 132 | } 133 | 134 | if (settings.resizeImage) { 135 | settings.imageMaxWidth = settings.imageMaxWidth || Infinity; 136 | settings.imageMaxHeight = settings.imageMaxHeight || Infinity; 137 | } 138 | 139 | if (!settings.resizeImage) { 140 | if (settings.fileType && settings.fileType.test) { 141 | // Not using MIME types 142 | if (!settings.fileType.test(info.name.substr(info.name.lastIndexOf('.')+1))) { 143 | log('ERROR: Invalid Filetype.'); 144 | settings.fileError.call(this, info, 'INVALID_FILETYPE', 'Invalid filetype.'); 145 | return; 146 | } 147 | } 148 | 149 | if (settings.fileMaxSize && file.size > settings.fileMaxSize) { 150 | log('ERROR: File exceeds size limit.'); 151 | settings.fileError.call(this, info, 'FILE_EXCEEDS_SIZE_LIMIT', 'File exceeds size limit.'); 152 | return; 153 | } 154 | } 155 | 156 | if (!settings.resizeImage && canReadFormData) { 157 | log('INFO: Bypass file reading, insert file object into FormData object directly.'); 158 | handleForm(settings, 'file', file, info); 159 | } else if (window.FileReader) { 160 | log('INFO: Using FileReader to do asynchronously file reading.'); 161 | var reader = new FileReader(); 162 | reader.onerror = function (ev) { 163 | if (ev.target.error) { 164 | switch (ev.target.error) { 165 | case 8: 166 | log('ERROR: File not found.'); 167 | settings.fileError.call(this, info, 'FILE_NOT_FOUND', 'File not found.'); 168 | break; 169 | case 24: 170 | log('ERROR: File not readable.'); 171 | settings.fileError.call(this, info, 'IO_ERROR', 'File not readable.'); 172 | break; 173 | case 18: 174 | log('ERROR: File cannot be access due to security constrant.'); 175 | settings.fileError.call(this, info, 'SECURITY_ERROR', 'File cannot be access due to security constrant.'); 176 | break; 177 | case 20: //User Abort 178 | break; 179 | } 180 | } 181 | } 182 | if (!settings.resizeImage) { 183 | if (canSendBinaryString) { 184 | reader.onloadend = function (ev) { 185 | var bin = ev.target.result; 186 | handleForm(settings, 'bin', bin, info); 187 | }; 188 | reader.readAsBinaryString(file); 189 | } else if (settings.allowDataInBase64) { 190 | reader.onloadend = function (ev) { 191 | handleForm( 192 | settings, 193 | 'base64', 194 | dataURLtoBase64(ev.target.result), 195 | info 196 | ); 197 | }; 198 | reader.readAsDataURL(file); 199 | } else { 200 | log('ERROR: No available method to extract file; allowDataInBase64 not set.'); 201 | settings.fileError.call(this, info, 'NO_BIN_SUPPORT_AND_BASE64_NOT_SET', 'No available method to extract file; allowDataInBase64 not set.'); 202 | } 203 | } else { 204 | reader.onloadend = function (ev) { 205 | var dataurl = ev.target.result; 206 | handleImage(settings, dataurl, info); 207 | }; 208 | reader.readAsDataURL(file); 209 | } 210 | } else if (window.File.prototype.getAsBinary) { 211 | log('WARN: FileReader does not exist, UI will be blocked when reading big file.'); 212 | if (!settings.resizeImage) { 213 | try { 214 | var bin = file.getAsBinary(); 215 | } catch (e) { 216 | log('ERROR: File not readable.'); 217 | settings.fileError.call(this, info, 'IO_ERROR', 'File not readable.'); 218 | return; 219 | } 220 | handleForm(settings, 'bin', bin, info); 221 | } else { 222 | try { 223 | var bin = file.getAsDataURL(); 224 | } catch (e) { 225 | log('ERROR: File not readable.'); 226 | settings.fileError.call(this, info, 'IO_ERROR', 'File not readable.'); 227 | return; 228 | } 229 | handleImage(settings, dataurl, info); 230 | } 231 | } else { 232 | log('ERROR: No available method to extract file; this browser is not supported.'); 233 | settings.fileError.call(this, info, 'NOT_SUPPORT', 'ERROR: No available method to extract file; this browser is not supported.'); 234 | } 235 | }; 236 | 237 | // step 1.5: inject file into , paste the pixels into , 238 | // read the final image 239 | var handleImage = function (settings, dataurl, info) { 240 | var img = new Image(); 241 | img.onerror = function () { 242 | log('ERROR: failed to load, file is not a supported image format.'); 243 | settings.fileError.call(this, info, 'FILE_NOT_IMAGE', 'File is not a supported image format.'); 244 | }; 245 | img.onload = function () { 246 | var ratio = Math.max( 247 | img.width/settings.imageMaxWidth, 248 | img.height/settings.imageMaxHeight, 249 | 1 250 | ); 251 | var d = { 252 | w: Math.floor(Math.max(img.width/ratio, 1)), 253 | h: Math.floor(Math.max(img.height/ratio, 1)) 254 | } 255 | log( 256 | 'INFO: Original image size: ' + img.width.toString(10) + 'x' + img.height.toString(10) 257 | + ', resized image size: ' + d.w + 'x' + d.h + '.' 258 | ); 259 | if (!settings.forceResize && img.width === d.w && img.height === d.h) { 260 | log('INFO: Image demension is the same, send the original file.'); 261 | if (canResizeImageToBinaryString) { 262 | handleForm( 263 | settings, 264 | 'bin', 265 | window.atob(dataURLtoBase64(dataurl)), 266 | info 267 | ); 268 | } else if (settings.allowDataInBase64) { 269 | handleForm( 270 | settings, 271 | 'base64', 272 | dataURLtoBase64(dataurl), 273 | info 274 | ); 275 | } else { 276 | log('ERROR: No available method to send the original file; allowDataInBase64 not set.'); 277 | settings.fileError.call(this, info, 'NO_BIN_SUPPORT_AND_BASE64_NOT_SET', 'No available method to extract file; allowDataInBase64 not set.'); 278 | } 279 | return; 280 | } 281 | var canvas = document.createElement('canvas'); 282 | canvas.setAttribute('width', d.w); 283 | canvas.setAttribute('height', d.h); 284 | canvas.getContext('2d').drawImage( 285 | img, 286 | 0, 287 | 0, 288 | img.width, 289 | img.height, 290 | 0, 291 | 0, 292 | d.w, 293 | d.h 294 | ); 295 | if (!settings.imageType || settings.imageType === 'auto') { 296 | if (info.type === 'image/jpeg') settings.imageType = 'jpeg'; 297 | else settings.imageType = 'png'; 298 | } 299 | 300 | var ninfo = { 301 | type: 'image/' + settings.imageType, 302 | name: info.name.substr(0, info.name.indexOf('.')) + '.resized.' + settings.imageType 303 | }; 304 | 305 | if (canResizeImageToFile && canSendFormData) { 306 | // Gecko 2 (Fx4) non-standard function 307 | var nfile = canvas.mozGetAsFile( 308 | ninfo.name, 309 | 'image/' + settings.imageType 310 | ); 311 | ninfo.size = file.size || file.fileSize; 312 | handleForm( 313 | settings, 314 | 'file', 315 | nfile, 316 | ninfo 317 | ); 318 | } else if (canResizeImageToBinaryString && canSendBinaryString) { 319 | // Read the image as DataURL, convert it back to binary string. 320 | var bin = window.atob(dataURLtoBase64(canvas.toDataURL('image/' + settings.imageType))); 321 | ninfo.size = bin.length; 322 | handleForm( 323 | settings, 324 | 'bin', 325 | bin, 326 | ninfo 327 | ); 328 | } else if (settings.allowDataInBase64 && canResizeImageToBase64 && canSendImageInBase64) { 329 | handleForm( 330 | settings, 331 | 'base64', 332 | dataURLtoBase64(canvas.toDataURL('image/' + settings.imageType)), 333 | ninfo 334 | ); 335 | } else { 336 | log('ERROR: No available method to extract image; allowDataInBase64 not set.'); 337 | settings.fileError.call(this, info, 'NO_BIN_SUPPORT_AND_BASE64_NOT_SET', 'No available method to extract file; allowDataInBase64 not set.'); 338 | } 339 | } 340 | img.src = dataurl; 341 | } 342 | // Step 2: construct form data and send the file 343 | // paramaters: Ajax settings, File object, binary string of file || null, file info assoc array 344 | var handleForm = function (settings, type, data, info) { 345 | if (canSendFormData && type === 'file') { 346 | // FormData API saves the day 347 | log('INFO: Using FormData to construct form.'); 348 | var formdata = new FormData(); 349 | formdata.append('Filedata', data); 350 | // Prevent jQuery form convert FormData object into string. 351 | settings.processData = false; 352 | // Prevent jQuery from overwrite automatically generated xhr content-Type header 353 | // by unsetting the default contentType and inject data only right before xhr.send() 354 | settings.contentType = null; 355 | settings.__beforeSend = settings.beforeSend; 356 | settings.beforeSend = function (xhr, s) { 357 | s.data = formdata; 358 | if (s.__beforeSend) return s.__beforeSend.call(this, xhr, s); 359 | } 360 | //settings.data = formdata; 361 | } else if (canSendBinaryString && type === 'bin') { 362 | log('INFO: Concat our own multipart/form-data data string.'); 363 | 364 | // A placeholder MIME type 365 | if (!info.type) info.type = 'application/octet-stream'; 366 | 367 | if (/[^\x20-\x7E]/.test(info.name)) { 368 | log('INFO: Filename contains non-ASCII code, do UTF8-binary string conversion.'); 369 | info.name_bin = unescape(encodeURIComponent(info.name)); 370 | } 371 | 372 | //filtered out non-ASCII chars in filenames 373 | // info.name = info.name.replace(/[^\x20-\x7E]/g, '_'); 374 | 375 | // multipart/form-data boundary 376 | var bd = 'xhrupload-' + parseInt(Math.random()*(2 << 16)); 377 | settings.contentType = 'multipart/form-data; boundary=' + bd; 378 | var formdata = '--' + bd + '\n' // RFC 1867 Format, simulate form file upload 379 | + 'content-disposition: form-data; name="Filedata";' 380 | + ' filename="' + (info.name_bin || info.name) + '"\n' 381 | + 'Content-Type: ' + info.type + '\n\n' 382 | + data + '\n\n' 383 | + '--' + bd + '--'; 384 | 385 | if (window.XMLHttpRequest.prototype.sendAsBinary) { 386 | // Use xhr.sendAsBinary that takes binary string 387 | log('INFO: Pass binary string to xhr.'); 388 | settings.data = formdata; 389 | } else { 390 | // make a blob 391 | log('INFO: Convert binary string into Blob.'); 392 | var buf = new ArrayBuffer(formdata.length); 393 | var view = new Uint8Array(buf); 394 | $.each( 395 | formdata, 396 | function (i, o) { 397 | view[i] = o.charCodeAt(0); 398 | } 399 | ); 400 | var bb = new BlobBuilder(); 401 | bb.append(buf); 402 | var blob = bb.getBlob(); 403 | 404 | settings.processData = false; 405 | settings.__beforeSend = settings.beforeSend; 406 | settings.beforeSend = function (xhr, s) { 407 | s.data = blob; 408 | if (s.__beforeSend) return s.__beforeSend.call(this, xhr, s); 409 | }; 410 | } 411 | 412 | } else if (settings.allowDataInBase64 && type === 'base64') { 413 | log('INFO: Concat our own multipart/form-data data string; send the file in base64 because binary xhr is not supported.'); 414 | 415 | // A placeholder MIME type 416 | if (!info.type) info.type = 'application/octet-stream'; 417 | 418 | // multipart/form-data boundary 419 | var bd = 'xhrupload-' + parseInt(Math.random()*(2 << 16)); 420 | settings.contentType = 'multipart/form-data; boundary=' + bd; 421 | settings.data = '--' + bd + '\n' // RFC 1867 Format, simulate form file upload 422 | + 'content-disposition: form-data; name="Filedata";' 423 | + ' filename="' + encodeURIComponent(info.name) + '.base64"\n' 424 | + 'Content-Transfer-Encoding: base64\n' // Vaild MIME header, but won't work with PHP file upload handling. 425 | + 'Content-Type: ' + info.type + '\n\n' 426 | + data + '\n\n' 427 | + '--' + bd + '--'; 428 | } else { 429 | log('ERROR: Data is not given in processable form.'); 430 | settings.fileError.call(this, info, 'INTERNAL_ERROR', 'Data is not given in processable form.'); 431 | return; 432 | } 433 | xhrupload(settings); 434 | }; 435 | 436 | // Step 3: start sending out file 437 | var xhrupload = function (settings) { 438 | log('INFO: Sending file.'); 439 | if (typeof settings.data === 'string' && canSendBinaryString) { 440 | log('INFO: Using xhr.sendAsBinary.'); 441 | settings.___beforeSend = settings.beforeSend; 442 | settings.beforeSend = function (xhr, s) { 443 | xhr.send = xhr.sendAsBinary; 444 | if (s.___beforeSend) return s.___beforeSend.call(this, xhr, s); 445 | } 446 | } 447 | $.ajax(settings); 448 | }; 449 | 450 | $.fn.fileUpload = function(settings) { 451 | this.each(function(i, el) { 452 | if ($(el).is('input[type=file]')) { 453 | log('INFO: binding onchange event to a input[type=file].'); 454 | $(el).bind( 455 | 'change', 456 | function () { 457 | if (!this.files.length) { 458 | log('ERROR: no file selected.'); 459 | return; 460 | } else if (this.files.length > 1) { 461 | log('WARN: Multiple file upload not implemented yet, only first file will be uploaded.'); 462 | } 463 | handleFile($.extend({}, config, settings), this.files[0]); 464 | 465 | if (this.form.length === 1) { 466 | this.form.reset(); 467 | } else { 468 | log('WARN: Unable to reset file selection, upload won\'t be triggered again if user selects the same file.'); 469 | } 470 | return; 471 | } 472 | ); 473 | } 474 | 475 | if ($(el).is('form')) { 476 | log('ERROR:
not implemented yet.'); 477 | } else { 478 | log('INFO: binding ondrop event.'); 479 | $(el).bind( 480 | 'dragover', // dragover behavior should be blocked for drop to invoke. 481 | function(ev) { 482 | return false; 483 | } 484 | ).bind( 485 | 'drop', 486 | function (ev) { 487 | if (!ev.originalEvent.dataTransfer.files) { 488 | log('ERROR: No FileList object present; user might had dropped text.'); 489 | return false; 490 | } 491 | if (!ev.originalEvent.dataTransfer.files.length) { 492 | log('ERROR: User had dropped a virual file (e.g. "My Computer")'); 493 | return false; 494 | } 495 | if (!ev.originalEvent.dataTransfer.files.length > 1) { 496 | log('WARN: Multiple file upload not implemented yet, only first file will be uploaded.'); 497 | } 498 | handleFile($.extend({}, config, settings), ev.originalEvent.dataTransfer.files[0]); 499 | return false; 500 | } 501 | ); 502 | } 503 | }); 504 | 505 | return this; 506 | }; 507 | 508 | $.fileUploadSupported = isSupported; 509 | $.imageUploadSupported = isImageSupported; 510 | $.fileUploadAsBase64Supported = isSupportedInBase64; 511 | $.imageUploadAsBase64Supported = isImageSupportedInBase64; 512 | 513 | })(jQuery); 514 | -------------------------------------------------------------------------------- /app/assets/javascripts/jquery.atwho.js: -------------------------------------------------------------------------------- 1 | // 2 | /* 3 | Implement Github like autocomplete mentions 4 | http://ichord.github.com/At.js 5 | 6 | Copyright (c) 2013 chord.luo@gmail.com 7 | Licensed under the MIT license. 8 | */ 9 | 10 | 11 | /* 12 | 本插件操作 textarea 或者 input 内的插入符 13 | 只实现了获得插入符在文本框中的位置,我设置 14 | 插入符的位置. 15 | */ 16 | 17 | 18 | (function() { 19 | (function(factory) { 20 | if (typeof define === 'function' && define.amd) { 21 | return define(['jquery'], factory); 22 | } else { 23 | return factory(window.jQuery); 24 | } 25 | })(function($) { 26 | "use strict"; 27 | var Caret, Mirror, methods, pluginName; 28 | 29 | pluginName = 'caret'; 30 | Caret = (function() { 31 | function Caret($inputor) { 32 | this.$inputor = $inputor; 33 | this.domInputor = this.$inputor[0]; 34 | } 35 | 36 | Caret.prototype.getPos = function() { 37 | var end, endRange, inputor, len, normalizedValue, pos, range, start, textInputRange; 38 | 39 | inputor = this.domInputor; 40 | inputor.focus(); 41 | if (document.selection) { 42 | /* 43 | #assume we select "HATE" in the inputor such as textarea -> { }. 44 | * start end-point. 45 | * / 46 | * < I really [HATE] IE > between the brackets is the selection range. 47 | * \ 48 | * end end-point. 49 | */ 50 | 51 | range = document.selection.createRange(); 52 | pos = 0; 53 | if (range && range.parentElement() === inputor) { 54 | normalizedValue = inputor.value.replace(/\r\n/g, "\n"); 55 | /* SOMETIME !!! 56 | "/r/n" is counted as two char. 57 | one line is two, two will be four. balalala. 58 | so we have to using the normalized one's length.; 59 | */ 60 | 61 | len = normalizedValue.length; 62 | /* 63 | <[ I really HATE IE ]>: 64 | the whole content in the inputor will be the textInputRange. 65 | */ 66 | 67 | textInputRange = inputor.createTextRange(); 68 | /* _here must be the position of bookmark. 69 | / 70 | <[ I really [HATE] IE ]> 71 | [---------->[ ] : this is what moveToBookmark do. 72 | < I really [[HATE] IE ]> : here is result. 73 | \ two brackets in should be in line. 74 | */ 75 | 76 | textInputRange.moveToBookmark(range.getBookmark()); 77 | endRange = inputor.createTextRange(); 78 | /* [--------------------->[] : if set false all end-point goto end. 79 | < I really [[HATE] IE []]> 80 | */ 81 | 82 | endRange.collapse(false); 83 | /* 84 | ___VS____ 85 | / \ 86 | < I really [[HATE] IE []]> 87 | \_endRange end-point. 88 | 89 | " > -1" mean the start end-point will be the same or right to the end end-point 90 | * simplelly, all in the end. 91 | */ 92 | 93 | if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { 94 | start = end = len; 95 | } else { 96 | /* 97 | I really |HATE] IE ]> 98 | <-| 99 | I really[ [HATE] IE ]> 100 | <-[ 101 | I reall[y [HATE] IE ]> 102 | 103 | will return how many unit have moved. 104 | */ 105 | 106 | start = -textInputRange.moveStart("character", -len); 107 | end = -textInputRange.moveEnd("character", -len); 108 | } 109 | } 110 | } else { 111 | start = inputor.selectionStart; 112 | } 113 | return start; 114 | }; 115 | 116 | Caret.prototype.setPos = function(pos) { 117 | var inputor, range; 118 | 119 | inputor = this.domInputor; 120 | if (document.selection) { 121 | range = inputor.createTextRange(); 122 | range.move("character", pos); 123 | return range.select(); 124 | } else { 125 | return inputor.setSelectionRange(pos, pos); 126 | } 127 | }; 128 | 129 | Caret.prototype.getPosition = function(pos) { 130 | var $inputor, at_rect, format, h, html, mirror, start_range, x, y; 131 | 132 | $inputor = this.$inputor; 133 | format = function(value) { 134 | return value.replace(//g, '>').replace(/`/g, '`').replace(/"/g, '"').replace(/\r\n|\r|\n/g, "
"); 135 | }; 136 | if (pos === void 0) { 137 | pos = this.getPos(); 138 | } 139 | start_range = $inputor.val().slice(0, pos); 140 | html = "" + format(start_range) + ""; 141 | html += "|"; 142 | mirror = new Mirror($inputor); 143 | at_rect = mirror.create(html).rect(); 144 | x = at_rect.left - $inputor.scrollLeft(); 145 | y = at_rect.top - $inputor.scrollTop(); 146 | h = at_rect.height; 147 | return { 148 | left: x, 149 | top: y, 150 | height: h 151 | }; 152 | }; 153 | 154 | Caret.prototype.getOffset = function(pos) { 155 | var $inputor, h, offset, position, x, y; 156 | 157 | $inputor = this.$inputor; 158 | offset = $inputor.offset(); 159 | position = this.getPosition(pos); 160 | x = offset.left + position.left; 161 | y = offset.top + position.top; 162 | h = position.height; 163 | return { 164 | left: x, 165 | top: y, 166 | height: h 167 | }; 168 | }; 169 | 170 | Caret.prototype.getIEPosition = function(pos) { 171 | var h, inputorOffset, offset, x, y; 172 | 173 | offset = this.getIEOffset(pos); 174 | inputorOffset = this.$inputor.offset(); 175 | x = offset.left - inputorOffset.left; 176 | y = offset.top - inputorOffset.top; 177 | h = offset.height; 178 | return { 179 | left: x, 180 | top: y, 181 | height: h 182 | }; 183 | }; 184 | 185 | Caret.prototype.getIEOffset = function(pos) { 186 | var h, range, x, y; 187 | 188 | range = this.domInputor.createTextRange(); 189 | if (pos) { 190 | range.move('character', pos); 191 | } 192 | x = range.boundingLeft + $inputor.scrollLeft(); 193 | y = range.boundingTop + $(window).scrollTop() + $inputor.scrollTop(); 194 | h = range.boundingHeight; 195 | return { 196 | left: x, 197 | top: y, 198 | height: h 199 | }; 200 | }; 201 | 202 | return Caret; 203 | 204 | })(); 205 | Mirror = (function() { 206 | Mirror.prototype.css_attr = ["overflowY", "height", "width", "paddingTop", "paddingLeft", "paddingRight", "paddingBottom", "marginTop", "marginLeft", "marginRight", "marginBottom", "fontFamily", "borderStyle", "borderWidth", "wordWrap", "fontSize", "lineHeight", "overflowX", "text-align"]; 207 | 208 | function Mirror($inputor) { 209 | this.$inputor = $inputor; 210 | } 211 | 212 | Mirror.prototype.mirrorCss = function() { 213 | var css, 214 | _this = this; 215 | 216 | css = { 217 | position: 'absolute', 218 | left: -9999, 219 | top: 0, 220 | zIndex: -20000, 221 | 'white-space': 'pre-wrap' 222 | }; 223 | $.each(this.css_attr, function(i, p) { 224 | return css[p] = _this.$inputor.css(p); 225 | }); 226 | return css; 227 | }; 228 | 229 | Mirror.prototype.create = function(html) { 230 | this.$mirror = $('
'); 231 | this.$mirror.css(this.mirrorCss()); 232 | this.$mirror.html(html); 233 | this.$inputor.after(this.$mirror); 234 | return this; 235 | }; 236 | 237 | Mirror.prototype.rect = function() { 238 | var $flag, pos, rect; 239 | 240 | $flag = this.$mirror.find("#caret"); 241 | pos = $flag.position(); 242 | rect = { 243 | left: pos.left, 244 | top: pos.top, 245 | height: $flag.height() 246 | }; 247 | this.$mirror.remove(); 248 | return rect; 249 | }; 250 | 251 | return Mirror; 252 | 253 | })(); 254 | methods = { 255 | pos: function(pos) { 256 | if (pos) { 257 | return this.setPos(pos); 258 | } else { 259 | return this.getPos(); 260 | } 261 | }, 262 | position: function(pos) { 263 | if (document.selection) { 264 | return this.getIEPosition(pos); 265 | } else { 266 | return this.getPosition(pos); 267 | } 268 | }, 269 | offset: function(pos) { 270 | if (document.selection) { 271 | return this.getIEOffset(pos); 272 | } else { 273 | return this.getOffset(pos); 274 | } 275 | } 276 | }; 277 | return $.fn.caret = function(method) { 278 | var caret; 279 | 280 | caret = new Caret(this); 281 | if (methods[method]) { 282 | return methods[method].apply(caret, Array.prototype.slice.call(arguments, 1)); 283 | } else { 284 | return $.error("Method " + method + " does not exist on jQuery.caret"); 285 | } 286 | }; 287 | }); 288 | 289 | }).call(this); 290 | 291 | 292 | /* 293 | Implement Github like autocomplete mentions 294 | http://ichord.github.com/At.js 295 | 296 | Copyright (c) 2013 chord.luo@gmail.com 297 | Licensed under the MIT license. 298 | */ 299 | 300 | 301 | (function() { 302 | var __slice = [].slice; 303 | 304 | (function(factory) { 305 | if (typeof define === 'function' && define.amd) { 306 | return define(['jquery'], factory); 307 | } else { 308 | return factory(window.jQuery); 309 | } 310 | })(function($) { 311 | var $CONTAINER, Api, App, Controller, DEFAULT_CALLBACKS, DEFAULT_TPL, KEY_CODE, Model, View; 312 | App = (function() { 313 | 314 | function App(inputor) { 315 | this.current_flag = null; 316 | this.controllers = {}; 317 | this.$inputor = $(inputor); 318 | this.listen(); 319 | } 320 | 321 | App.prototype.controller = function(key) { 322 | return this.controllers[key || this.current_flag]; 323 | }; 324 | 325 | App.prototype.set_context_for = function(key) { 326 | this.current_flag = key; 327 | return this; 328 | }; 329 | 330 | App.prototype.reg = function(flag, setting) { 331 | var controller, _base; 332 | controller = (_base = this.controllers)[flag] || (_base[flag] = new Controller(this, flag)); 333 | if (setting.alias) { 334 | this.controllers[setting.alias] = controller; 335 | } 336 | controller.init(setting); 337 | return this; 338 | }; 339 | 340 | App.prototype.listen = function() { 341 | var _this = this; 342 | return this.$inputor.on('keyup.atwho', function(e) { 343 | return _this.on_keyup(e); 344 | }).on('keydown.atwho', function(e) { 345 | return _this.on_keydown(e); 346 | }).on('scroll.atwho', function(e) { 347 | var _ref; 348 | return (_ref = _this.controller()) != null ? _ref.view.hide() : void 0; 349 | }).on('blur.atwho', function(e) { 350 | var c; 351 | if (c = _this.controller()) { 352 | return c.view.hide(c.get_opt("display_timeout")); 353 | } 354 | }); 355 | }; 356 | 357 | App.prototype.dispatch = function() { 358 | var _this = this; 359 | return $.map(this.controllers, function(c) { 360 | if (c.look_up()) { 361 | return _this.set_context_for(c.key); 362 | } 363 | }); 364 | }; 365 | 366 | App.prototype.on_keyup = function(e) { 367 | var _ref; 368 | switch (e.keyCode) { 369 | case KEY_CODE.ESC: 370 | e.preventDefault(); 371 | if ((_ref = this.controller()) != null) { 372 | _ref.view.hide(); 373 | } 374 | break; 375 | case KEY_CODE.DOWN: 376 | case KEY_CODE.UP: 377 | $.noop(); 378 | break; 379 | default: 380 | this.dispatch(); 381 | } 382 | }; 383 | 384 | App.prototype.on_keydown = function(e) { 385 | var view, _ref; 386 | view = (_ref = this.controller()) != null ? _ref.view : void 0; 387 | if (!(view && view.visible())) { 388 | return; 389 | } 390 | switch (e.keyCode) { 391 | case KEY_CODE.ESC: 392 | e.preventDefault(); 393 | view.hide(); 394 | break; 395 | case KEY_CODE.UP: 396 | e.preventDefault(); 397 | view.prev(); 398 | break; 399 | case KEY_CODE.DOWN: 400 | e.preventDefault(); 401 | view.next(); 402 | break; 403 | case KEY_CODE.TAB: 404 | case KEY_CODE.ENTER: 405 | if (!view.visible()) { 406 | return; 407 | } 408 | e.preventDefault(); 409 | view.choose(); 410 | break; 411 | default: 412 | $.noop(); 413 | } 414 | }; 415 | 416 | return App; 417 | 418 | })(); 419 | Controller = (function() { 420 | var uuid, _uuid; 421 | 422 | _uuid = 0; 423 | 424 | uuid = function() { 425 | return _uuid += 1; 426 | }; 427 | 428 | function Controller(app, key) { 429 | this.app = app; 430 | this.key = key; 431 | this.$inputor = this.app.$inputor; 432 | this.id = this.$inputor[0].id || uuid(); 433 | this.setting = null; 434 | this.query = null; 435 | this.pos = 0; 436 | $CONTAINER.append(this.$el = $("
")); 437 | this.model = new Model(this); 438 | this.view = new View(this); 439 | } 440 | 441 | Controller.prototype.init = function(setting) { 442 | this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting); 443 | return this.model.reload(this.setting.data); 444 | }; 445 | 446 | Controller.prototype.call_default = function() { 447 | var args, func_name; 448 | func_name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 449 | try { 450 | return DEFAULT_CALLBACKS[func_name].apply(this, args); 451 | } catch (error) { 452 | return $.error("" + error + " Or maybe At.js doesn't have function " + func_name); 453 | } 454 | }; 455 | 456 | Controller.prototype.trigger = function(name, data) { 457 | var alias, event_name; 458 | data.push(this); 459 | alias = this.get_opt('alias'); 460 | event_name = alias ? "" + name + "-" + alias + ".atwho" : "" + name + ".atwho"; 461 | return this.$inputor.trigger(event_name, data); 462 | }; 463 | 464 | Controller.prototype.callbacks = function(func_name) { 465 | return this.get_opt("callbacks")[func_name] || DEFAULT_CALLBACKS[func_name]; 466 | }; 467 | 468 | Controller.prototype.get_opt = function(key, default_value) { 469 | try { 470 | return this.setting[key]; 471 | } catch (e) { 472 | return null; 473 | } 474 | }; 475 | 476 | Controller.prototype.catch_query = function() { 477 | var caret_pos, content, end, query, start, subtext; 478 | content = this.$inputor.val(); 479 | caret_pos = this.$inputor.caret('pos'); 480 | subtext = content.slice(0, caret_pos); 481 | query = this.callbacks("matcher").call(this, this.key, subtext, this.get_opt('start_with_space')); 482 | if (typeof query === "string" && query.length <= this.get_opt('max_len', 20)) { 483 | start = caret_pos - query.length; 484 | end = start + query.length; 485 | this.pos = start; 486 | query = { 487 | 'text': query.toLowerCase(), 488 | 'head_pos': start, 489 | 'end_pos': end 490 | }; 491 | this.trigger("matched", [this.key, query.text]); 492 | } else { 493 | this.view.hide(); 494 | } 495 | return this.query = query; 496 | }; 497 | 498 | Controller.prototype.rect = function() { 499 | var c, scale_bottom; 500 | c = this.$inputor.caret('offset', this.pos - 1); 501 | scale_bottom = document.selection ? 0 : 2; 502 | return { 503 | left: c.left, 504 | top: c.top, 505 | bottom: c.top + c.height + scale_bottom 506 | }; 507 | }; 508 | 509 | Controller.prototype.insert = function(str) { 510 | var $inputor, source, start_str, text; 511 | $inputor = this.$inputor; 512 | str = '' + str; 513 | source = $inputor.val(); 514 | start_str = source.slice(0, this.query['head_pos'] || 0); 515 | text = "" + start_str + str + " " + (source.slice(this.query['end_pos'] || 0)); 516 | $inputor.val(text); 517 | $inputor.caret('pos', start_str.length + str.length + 1); 518 | return $inputor.change(); 519 | }; 520 | 521 | Controller.prototype.render_view = function(data) { 522 | var search_key; 523 | search_key = this.get_opt("search_key"); 524 | data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), search_key); 525 | return this.view.render(data.slice(0, this.get_opt('limit'))); 526 | }; 527 | 528 | Controller.prototype.look_up = function() { 529 | var query, _callback; 530 | if (!(query = this.catch_query())) { 531 | return; 532 | } 533 | _callback = function(data) { 534 | if (data && data.length > 0) { 535 | return this.render_view(data); 536 | } else { 537 | return this.view.hide(); 538 | } 539 | }; 540 | this.model.query(query.text, $.proxy(_callback, this)); 541 | return query; 542 | }; 543 | 544 | return Controller; 545 | 546 | })(); 547 | Model = (function() { 548 | var _storage; 549 | 550 | _storage = {}; 551 | 552 | function Model(context) { 553 | this.context = context; 554 | this.key = this.context.key; 555 | } 556 | 557 | Model.prototype.saved = function() { 558 | return this.fetch() > 0; 559 | }; 560 | 561 | Model.prototype.query = function(query, callback) { 562 | var data, search_key, _ref; 563 | data = this.fetch(); 564 | search_key = this.context.get_opt("search_key"); 565 | callback(data = this.context.callbacks('filter').call(this.context, query, data, search_key)); 566 | if (!(data && data.length > 0)) { 567 | return (_ref = this.context.callbacks('remote_filter')) != null ? _ref.call(this.context, query, callback) : void 0; 568 | } 569 | }; 570 | 571 | Model.prototype.fetch = function() { 572 | return _storage[this.key] || []; 573 | }; 574 | 575 | Model.prototype.save = function(data) { 576 | return _storage[this.key] = this.context.callbacks("before_save").call(this.context, data || []); 577 | }; 578 | 579 | Model.prototype.load = function(data) { 580 | if (!(this.saved() || !data)) { 581 | return this._load(data); 582 | } 583 | }; 584 | 585 | Model.prototype.reload = function(data) { 586 | return this._load(data); 587 | }; 588 | 589 | Model.prototype._load = function(data) { 590 | var _this = this; 591 | if (typeof data === "string") { 592 | return $.ajax(data, { 593 | dataType: "json" 594 | }).done(function(data) { 595 | return _this.save(data); 596 | }); 597 | } else { 598 | return this.save(data); 599 | } 600 | }; 601 | 602 | return Model; 603 | 604 | })(); 605 | View = (function() { 606 | 607 | function View(context) { 608 | this.context = context; 609 | this.key = this.context.key; 610 | this.id = this.context.get_opt("alias") || ("at-view-" + (this.key.charCodeAt(0))); 611 | this.$el = $("
    "); 612 | this.timeout_id = null; 613 | this.context.$el.append(this.$el); 614 | this.bind_event(); 615 | } 616 | 617 | View.prototype.bind_event = function() { 618 | var $menu, 619 | _this = this; 620 | $menu = this.$el.find('ul'); 621 | return $menu.on('mouseenter.view', 'li', function(e) { 622 | $menu.find('.cur').removeClass('cur'); 623 | return $(e.currentTarget).addClass('cur'); 624 | }).on('click', function(e) { 625 | _this.choose(); 626 | return e.preventDefault(); 627 | }); 628 | }; 629 | 630 | View.prototype.visible = function() { 631 | return this.$el.is(":visible"); 632 | }; 633 | 634 | View.prototype.choose = function() { 635 | var $li; 636 | $li = this.$el.find(".cur"); 637 | this.context.insert(this.context.callbacks("before_insert").call(this.context, $li.data("value"), $li)); 638 | this.context.trigger("inserted", [$li]); 639 | return this.hide(); 640 | }; 641 | 642 | View.prototype.reposition = function() { 643 | var offset, rect; 644 | rect = this.context.rect(); 645 | if (rect.bottom + this.$el.height() - $(window).scrollTop() > $(window).height()) { 646 | rect.bottom = rect.top - this.$el.height(); 647 | } 648 | offset = { 649 | left: rect.left, 650 | top: rect.bottom 651 | }; 652 | this.$el.offset(offset); 653 | return this.context.trigger("reposition", [offset]); 654 | }; 655 | 656 | View.prototype.next = function() { 657 | var cur, next; 658 | cur = this.$el.find('.cur').removeClass('cur'); 659 | next = cur.next(); 660 | if (!next.length) { 661 | next = this.$el.find('li:first'); 662 | } 663 | return next.addClass('cur'); 664 | }; 665 | 666 | View.prototype.prev = function() { 667 | var cur, prev; 668 | cur = this.$el.find('.cur').removeClass('cur'); 669 | prev = cur.prev(); 670 | if (!prev.length) { 671 | prev = this.$el.find('li:last'); 672 | } 673 | return prev.addClass('cur'); 674 | }; 675 | 676 | View.prototype.show = function() { 677 | if (!this.visible()) { 678 | this.$el.show(); 679 | } 680 | return this.reposition(); 681 | }; 682 | 683 | View.prototype.hide = function(time) { 684 | var callback, 685 | _this = this; 686 | if (isNaN(time && this.visible())) { 687 | return this.$el.hide(); 688 | } else { 689 | callback = function() { 690 | return _this.hide(); 691 | }; 692 | clearTimeout(this.timeout_id); 693 | return this.timeout_id = setTimeout(callback, time); 694 | } 695 | }; 696 | 697 | View.prototype.render = function(list) { 698 | var $li, $ul, item, li, tpl, _i, _len; 699 | if (!$.isArray(list || list.length <= 0)) { 700 | this.hide(); 701 | return; 702 | } 703 | this.$el.find('ul').empty(); 704 | $ul = this.$el.find('ul'); 705 | tpl = this.context.get_opt('tpl', DEFAULT_TPL); 706 | for (_i = 0, _len = list.length; _i < _len; _i++) { 707 | item = list[_i]; 708 | li = this.context.callbacks("tpl_eval").call(this.context, tpl, item); 709 | $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text)); 710 | $li.data("atwho-info", item); 711 | $ul.append($li); 712 | } 713 | this.show(); 714 | return $ul.find("li:first").addClass("cur"); 715 | }; 716 | 717 | return View; 718 | 719 | })(); 720 | KEY_CODE = { 721 | DOWN: 40, 722 | UP: 38, 723 | ESC: 27, 724 | TAB: 9, 725 | ENTER: 13 726 | }; 727 | DEFAULT_CALLBACKS = { 728 | before_save: function(data) { 729 | var item, _i, _len, _results; 730 | if (!$.isArray(data)) { 731 | return data; 732 | } 733 | _results = []; 734 | for (_i = 0, _len = data.length; _i < _len; _i++) { 735 | item = data[_i]; 736 | if ($.isPlainObject(item)) { 737 | _results.push(item); 738 | } else { 739 | _results.push({ 740 | name: item 741 | }); 742 | } 743 | } 744 | return _results; 745 | }, 746 | matcher: function(flag, subtext, should_start_with_space) { 747 | var match, regexp; 748 | flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 749 | if (should_start_with_space) { 750 | flag = '(?:^|\\s)' + flag; 751 | } 752 | regexp = new RegExp(flag + '([A-Za-z0-9_\+\-]*)$|' + flag + '([^\\x00-\\xff]*)$', 'gi'); 753 | match = regexp.exec(subtext); 754 | if (match) { 755 | return match[2] || match[1]; 756 | } else { 757 | return null; 758 | } 759 | }, 760 | filter: function(query, data, search_key) { 761 | var item, _i, _len, _results; 762 | _results = []; 763 | for (_i = 0, _len = data.length; _i < _len; _i++) { 764 | item = data[_i]; 765 | if (~item[search_key].toLowerCase().indexOf(query)) { 766 | _results.push(item); 767 | } 768 | } 769 | return _results; 770 | }, 771 | remote_filter: null, 772 | sorter: function(query, items, search_key) { 773 | var item, _i, _len, _results; 774 | if (!query) { 775 | return items; 776 | } 777 | _results = []; 778 | for (_i = 0, _len = items.length; _i < _len; _i++) { 779 | item = items[_i]; 780 | item.atwho_order = item[search_key].toLowerCase().indexOf(query); 781 | if (item.atwho_order > -1) { 782 | _results.push(item); 783 | } 784 | } 785 | return _results.sort(function(a, b) { 786 | return a.atwho_order - b.atwho_order; 787 | }); 788 | }, 789 | tpl_eval: function(tpl, map) { 790 | try { 791 | return tpl.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) { 792 | return map[key]; 793 | }); 794 | } catch (error) { 795 | return ""; 796 | } 797 | }, 798 | highlighter: function(li, query) { 799 | var regexp; 800 | if (!query) { 801 | return li; 802 | } 803 | regexp = new RegExp(">\\s*(\\w*)(" + query.replace("+", "\\+") + ")(\\w*)\\s*<", 'ig'); 804 | return li.replace(regexp, function(str, $1, $2, $3) { 805 | return '> ' + $1 + '' + $2 + '' + $3 + ' <'; 806 | }); 807 | }, 808 | before_insert: function(value, $li) { 809 | return value; 810 | } 811 | }; 812 | DEFAULT_TPL = "
  • ${name}
  • "; 813 | Api = { 814 | init: function(options) { 815 | var $this, app; 816 | app = ($this = $(this)).data("atwho"); 817 | if (!app) { 818 | $this.data('atwho', (app = new App(this))); 819 | } 820 | return app.reg(options.at, options); 821 | }, 822 | load: function(key, data) { 823 | var c; 824 | if (c = this.controller(key)) { 825 | return c.model.load(data); 826 | } 827 | }, 828 | run: function() { 829 | return this.dispatch(); 830 | } 831 | }; 832 | $CONTAINER = $("
    "); 833 | $.fn.atwho = function(method) { 834 | var _args; 835 | _args = arguments; 836 | $('body').append($CONTAINER); 837 | return this.filter('textarea, input').each(function() { 838 | var app; 839 | if (typeof method === 'object' || !method) { 840 | return Api.init.apply(this, _args); 841 | } else if (Api[method]) { 842 | if (app = $(this).data('atwho')) { 843 | return Api[method].apply(app, Array.prototype.slice.call(_args, 1)); 844 | } 845 | } else { 846 | return $.error("Method " + method + " does not exist on jQuery.caret"); 847 | } 848 | }); 849 | }; 850 | return $.fn.atwho["default"] = { 851 | at: void 0, 852 | alias: void 0, 853 | data: null, 854 | tpl: DEFAULT_TPL, 855 | callbacks: DEFAULT_CALLBACKS, 856 | search_key: "name", 857 | limit: 5, 858 | max_len: 20, 859 | start_with_space: true, 860 | display_timeout: 300 861 | }; 862 | }); 863 | 864 | }).call(this); 865 | -------------------------------------------------------------------------------- /app/assets/stylesheets/variables.scss: -------------------------------------------------------------------------------- 1 | // Override Bootstrap variables here (defaults from bootstrap-sass v3.3.4): 2 | 3 | // 4 | // Variables 5 | // -------------------------------------------------- 6 | 7 | 8 | //== Colors 9 | // 10 | //## Gray and brand colors for use across Bootstrap. 11 | 12 | // $gray-base: #000 13 | // $gray-darker: lighten($gray-base, 13.5%) // #222 14 | // $gray-dark: lighten($gray-base, 20%) // #333 15 | // $gray: lighten($gray-base, 33.5%) // #555 16 | // $gray-light: lighten($gray-base, 46.7%) // #777 17 | // $gray-lighter: lighten($gray-base, 93.5%) // #eee 18 | 19 | // $brand-primary: darken(#428bca, 6.5%) // #337ab7 20 | // $brand-success: #5cb85c 21 | // $brand-info: #5bc0de 22 | // $brand-warning: #f0ad4e 23 | // $brand-danger: #d9534f 24 | 25 | 26 | //== Scaffolding 27 | // 28 | //## Settings for some of the most global styles. 29 | 30 | //** Background color for ``. 31 | // $body-bg: #fff 32 | //** Global text color on ``. 33 | // $text-color: $gray-dark 34 | 35 | //** Global textual link color. 36 | // $link-color: $brand-primary 37 | //** Link hover color set via `darken()` function. 38 | // $link-hover-color: darken($link-color, 15%) 39 | //** Link hover decoration. 40 | // $link-hover-decoration: underline 41 | 42 | 43 | //== Typography 44 | // 45 | //## Font, line-height, and color for body text, headings, and more. 46 | 47 | // $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif 48 | // $font-family-serif: Georgia, "Times New Roman", Times, serif 49 | //** Default monospace fonts for ``, ``, and `
    `.
     50 | // $font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace
     51 | // $font-family-base:        $font-family-sans-serif
     52 | 
     53 | // $font-size-base:          14px
     54 | // $font-size-large:         ceil(($font-size-base * 1.25)) // ~18px
     55 | // $font-size-small:         ceil(($font-size-base * 0.85)) // ~12px
     56 | 
     57 | // $font-size-h1:            floor(($font-size-base * 2.6)) // ~36px
     58 | // $font-size-h2:            floor(($font-size-base * 2.15)) // ~30px
     59 | // $font-size-h3:            ceil(($font-size-base * 1.7)) // ~24px
     60 | // $font-size-h4:            ceil(($font-size-base * 1.25)) // ~18px
     61 | // $font-size-h5:            $font-size-base
     62 | // $font-size-h6:            ceil(($font-size-base * 0.85)) // ~12px
     63 | 
     64 | //** Unit-less `line-height` for use in components like buttons.
     65 | // $line-height-base:        1.428571429 // 20/14
     66 | //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
     67 | // $line-height-computed:    floor(($font-size-base * $line-height-base)) // ~20px
     68 | 
     69 | //** By default, this inherits from the ``.
     70 | // $headings-font-family:    inherit
     71 | // $headings-font-weight:    500
     72 | // $headings-line-height:    1.1
     73 | // $headings-color:          inherit
     74 | 
     75 | 
     76 | //== Iconography
     77 | //
     78 | //## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
     79 | 
     80 | //** Load fonts from this directory.
     81 | 
     82 | // [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
     83 | // [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
     84 | // $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/")
     85 | 
     86 | //** File name for all font files.
     87 | // $icon-font-name:          "glyphicons-halflings-regular"
     88 | //** Element ID within SVG icon file.
     89 | // $icon-font-svg-id:        "glyphicons_halflingsregular"
     90 | 
     91 | 
     92 | //== Components
     93 | //
     94 | //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
     95 | 
     96 | // $padding-base-vertical:     6px
     97 | // $padding-base-horizontal:   12px
     98 | 
     99 | // $padding-large-vertical:    10px
    100 | // $padding-large-horizontal:  16px
    101 | 
    102 | // $padding-small-vertical:    5px
    103 | // $padding-small-horizontal:  10px
    104 | 
    105 | // $padding-xs-vertical:       1px
    106 | // $padding-xs-horizontal:     5px
    107 | 
    108 | // $line-height-large:         1.3333333 // extra decimals for Win 8.1 Chrome
    109 | // $line-height-small:         1.5
    110 | 
    111 | // $border-radius-base:        4px
    112 | // $border-radius-large:       6px
    113 | // $border-radius-small:       3px
    114 | 
    115 | //** Global color for active items (e.g., navs or dropdowns).
    116 | // $component-active-color:    #fff
    117 | //** Global background color for active items (e.g., navs or dropdowns).
    118 | // $component-active-bg:       $brand-primary
    119 | 
    120 | //** Width of the `border` for generating carets that indicator dropdowns.
    121 | // $caret-width-base:          4px
    122 | //** Carets increase slightly in size for larger components.
    123 | // $caret-width-large:         5px
    124 | 
    125 | 
    126 | //== Tables
    127 | //
    128 | //## Customizes the `.table` component with basic values, each used across all table variations.
    129 | 
    130 | //** Padding for ``s and ``s.
    131 | // $table-cell-padding:            8px
    132 | //** Padding for cells in `.table-condensed`.
    133 | // $table-condensed-cell-padding:  5px
    134 | 
    135 | //** Default background color used for all tables.
    136 | // $table-bg:                      transparent
    137 | //** Background color used for `.table-striped`.
    138 | // $table-bg-accent:               #f9f9f9
    139 | //** Background color used for `.table-hover`.
    140 | // $table-bg-hover:                #f5f5f5
    141 | // $table-bg-active:               $table-bg-hover
    142 | 
    143 | //** Border color for table and cell borders.
    144 | // $table-border-color:            #ddd
    145 | 
    146 | 
    147 | //== Buttons
    148 | //
    149 | //## For each of Bootstrap's buttons, define text, background and border color.
    150 | 
    151 | // $btn-font-weight:                normal
    152 | 
    153 | // $btn-default-color:              #333
    154 | // $btn-default-bg:                 #fff
    155 | // $btn-default-border:             #ccc
    156 | 
    157 | // $btn-primary-color:              #fff
    158 | // $btn-primary-bg:                 $brand-primary
    159 | // $btn-primary-border:             darken($btn-primary-bg, 5%)
    160 | 
    161 | // $btn-success-color:              #fff
    162 | // $btn-success-bg:                 $brand-success
    163 | // $btn-success-border:             darken($btn-success-bg, 5%)
    164 | 
    165 | // $btn-info-color:                 #fff
    166 | // $btn-info-bg:                    $brand-info
    167 | // $btn-info-border:                darken($btn-info-bg, 5%)
    168 | 
    169 | // $btn-warning-color:              #fff
    170 | // $btn-warning-bg:                 $brand-warning
    171 | // $btn-warning-border:             darken($btn-warning-bg, 5%)
    172 | 
    173 | // $btn-danger-color:               #fff
    174 | // $btn-danger-bg:                  $brand-danger
    175 | // $btn-danger-border:              darken($btn-danger-bg, 5%)
    176 | 
    177 | // $btn-link-disabled-color:        $gray-light
    178 | 
    179 | 
    180 | //== Forms
    181 | //
    182 | //##
    183 | 
    184 | //** `` background color
    185 | // $input-bg:                       #fff
    186 | //** `` background color
    187 | // $input-bg-disabled:              $gray-lighter
    188 | 
    189 | //** Text color for ``s
    190 | // $input-color:                    $gray
    191 | //** `` border color
    192 | // $input-border:                   #ccc
    193 | 
    194 | // TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
    195 | //** Default `.form-control` border radius
    196 | // This has no effect on ``s in CSS.
    197 | // $input-border-radius:            $border-radius-base
    198 | //** Large `.form-control` border radius
    199 | // $input-border-radius-large:      $border-radius-large
    200 | //** Small `.form-control` border radius
    201 | // $input-border-radius-small:      $border-radius-small
    202 | 
    203 | //** Border color for inputs on focus
    204 | // $input-border-focus:             #66afe9
    205 | 
    206 | //** Placeholder text color
    207 | // $input-color-placeholder:        #999
    208 | 
    209 | //** Default `.form-control` height
    210 | // $input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 2)
    211 | //** Large `.form-control` height
    212 | // $input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2)
    213 | //** Small `.form-control` height
    214 | // $input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2)
    215 | 
    216 | //** `.form-group` margin
    217 | // $form-group-margin-bottom:       15px
    218 | 
    219 | // $legend-color:                   $gray-dark
    220 | // $legend-border-color:            #e5e5e5
    221 | 
    222 | //** Background color for textual input addons
    223 | // $input-group-addon-bg:           $gray-lighter
    224 | //** Border color for textual input addons
    225 | // $input-group-addon-border-color: $input-border
    226 | 
    227 | //** Disabled cursor for form controls and buttons.
    228 | // $cursor-disabled:                not-allowed
    229 | 
    230 | 
    231 | //== Dropdowns
    232 | //
    233 | //## Dropdown menu container and contents.
    234 | 
    235 | //** Background for the dropdown menu.
    236 | // $dropdown-bg:                    #fff
    237 | //** Dropdown menu `border-color`.
    238 | // $dropdown-border:                rgba(0,0,0,.15)
    239 | //** Dropdown menu `border-color` **for IE8**.
    240 | // $dropdown-fallback-border:       #ccc
    241 | //** Divider color for between dropdown items.
    242 | // $dropdown-divider-bg:            #e5e5e5
    243 | 
    244 | //** Dropdown link text color.
    245 | // $dropdown-link-color:            $gray-dark
    246 | //** Hover color for dropdown links.
    247 | // $dropdown-link-hover-color:      darken($gray-dark, 5%)
    248 | //** Hover background for dropdown links.
    249 | // $dropdown-link-hover-bg:         #f5f5f5
    250 | 
    251 | //** Active dropdown menu item text color.
    252 | // $dropdown-link-active-color:     $component-active-color
    253 | //** Active dropdown menu item background color.
    254 | // $dropdown-link-active-bg:        $component-active-bg
    255 | 
    256 | //** Disabled dropdown menu item background color.
    257 | // $dropdown-link-disabled-color:   $gray-light
    258 | 
    259 | //** Text color for headers within dropdown menus.
    260 | // $dropdown-header-color:          $gray-light
    261 | 
    262 | //** Deprecated `$dropdown-caret-color` as of v3.1.0
    263 | // $dropdown-caret-color:           #000
    264 | 
    265 | 
    266 | //-- Z-index master list
    267 | //
    268 | // Warning: Avoid customizing these values. They're used for a bird's eye view
    269 | // of components dependent on the z-axis and are designed to all work together.
    270 | //
    271 | // Note: These variables are not generated into the Customizer.
    272 | 
    273 | // $zindex-navbar:            1000
    274 | // $zindex-dropdown:          1000
    275 | // $zindex-popover:           1060
    276 | // $zindex-tooltip:           1070
    277 | // $zindex-navbar-fixed:      1030
    278 | // $zindex-modal-background:  1040
    279 | // $zindex-modal:             1050
    280 | 
    281 | 
    282 | //== Media queries breakpoints
    283 | //
    284 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
    285 | 
    286 | // Extra small screen / phone
    287 | //** Deprecated `$screen-xs` as of v3.0.1
    288 | // $screen-xs:                  480px
    289 | //** Deprecated `$screen-xs-min` as of v3.2.0
    290 | // $screen-xs-min:              $screen-xs
    291 | //** Deprecated `$screen-phone` as of v3.0.1
    292 | // $screen-phone:               $screen-xs-min
    293 | 
    294 | // Small screen / tablet
    295 | //** Deprecated `$screen-sm` as of v3.0.1
    296 | // $screen-sm:                  768px
    297 | // $screen-sm-min:              $screen-sm
    298 | //** Deprecated `$screen-tablet` as of v3.0.1
    299 | // $screen-tablet:              $screen-sm-min
    300 | 
    301 | // Medium screen / desktop
    302 | //** Deprecated `$screen-md` as of v3.0.1
    303 | // $screen-md:                  992px
    304 | // $screen-md-min:              $screen-md
    305 | //** Deprecated `$screen-desktop` as of v3.0.1
    306 | // $screen-desktop:             $screen-md-min
    307 | 
    308 | // Large screen / wide desktop
    309 | //** Deprecated `$screen-lg` as of v3.0.1
    310 | // $screen-lg:                  1200px
    311 | // $screen-lg-min:              $screen-lg
    312 | //** Deprecated `$screen-lg-desktop` as of v3.0.1
    313 | // $screen-lg-desktop:          $screen-lg-min
    314 | 
    315 | // So media queries don't overlap when required, provide a maximum
    316 | // $screen-xs-max:              ($screen-sm-min - 1)
    317 | // $screen-sm-max:              ($screen-md-min - 1)
    318 | // $screen-md-max:              ($screen-lg-min - 1)
    319 | 
    320 | 
    321 | //== Grid system
    322 | //
    323 | //## Define your custom responsive grid.
    324 | 
    325 | //** Number of columns in the grid.
    326 | // $grid-columns:              12
    327 | //** Padding between columns. Gets divided in half for the left and right.
    328 | // $grid-gutter-width:         30px
    329 | // Navbar collapse
    330 | //** Point at which the navbar becomes uncollapsed.
    331 | // $grid-float-breakpoint:     $screen-sm-min
    332 | //** Point at which the navbar begins collapsing.
    333 | // $grid-float-breakpoint-max: ($grid-float-breakpoint - 1)
    334 | 
    335 | 
    336 | //== Container sizes
    337 | //
    338 | //## Define the maximum width of `.container` for different screen sizes.
    339 | 
    340 | // Small screen / tablet
    341 | // $container-tablet:             (720px + $grid-gutter-width)
    342 | //** For `$screen-sm-min` and up.
    343 | // $container-sm:                 $container-tablet
    344 | 
    345 | // Medium screen / desktop
    346 | // $container-desktop:            (940px + $grid-gutter-width)
    347 | //** For `$screen-md-min` and up.
    348 | // $container-md:                 $container-desktop
    349 | 
    350 | // Large screen / wide desktop
    351 | // $container-large-desktop:      (1140px + $grid-gutter-width)
    352 | //** For `$screen-lg-min` and up.
    353 | // $container-lg:                 $container-large-desktop
    354 | 
    355 | 
    356 | //== Navbar
    357 | //
    358 | //##
    359 | 
    360 | // Basics of a navbar
    361 | $navbar-height:                    150px;
    362 | // $navbar-margin-bottom:             $line-height-computed
    363 | // $navbar-border-radius:             $border-radius-base
    364 | // $navbar-padding-horizontal:        floor(($grid-gutter-width / 2))
    365 | // $navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2)
    366 | // $navbar-collapse-max-height:       340px
    367 | 
    368 | $navbar-default-color:             red;
    369 | // $navbar-default-bg:                #f8f8f8
    370 | //$navbar-default-border:            darken($navbar-default-bg, 6.5%)
    371 | $navbar-default-border:            none;
    372 | 
    373 | // Navbar links
    374 | // $navbar-default-link-color:                #777
    375 | // $navbar-default-link-hover-color:          #333
    376 | // $navbar-default-link-hover-bg:             transparent
    377 | // $navbar-default-link-active-color:         #555
    378 | // $navbar-default-link-active-bg:            darken($navbar-default-bg, 6.5%)
    379 | // $navbar-default-link-disabled-color:       #ccc
    380 | // $navbar-default-link-disabled-bg:          transparent
    381 | 
    382 | // Navbar brand label
    383 | // $navbar-default-brand-color:               $navbar-default-link-color
    384 | // $navbar-default-brand-hover-color:         darken($navbar-default-brand-color, 10%)
    385 | // $navbar-default-brand-hover-bg:            transparent
    386 | 
    387 | // Navbar toggle
    388 | // $navbar-default-toggle-hover-bg:           #ddd
    389 | // $navbar-default-toggle-icon-bar-bg:        #888
    390 | // $navbar-default-toggle-border-color:       #ddd
    391 | 
    392 | 
    393 | // Inverted navbar
    394 | // Reset inverted navbar basics
    395 | // $navbar-inverse-color:                      lighten($gray-light, 15%)
    396 | // $navbar-inverse-bg:                         #222
    397 | // $navbar-inverse-border:                     darken($navbar-inverse-bg, 10%)
    398 | 
    399 | // Inverted navbar links
    400 | // $navbar-inverse-link-color:                 lighten($gray-light, 15%)
    401 | // $navbar-inverse-link-hover-color:           #fff
    402 | // $navbar-inverse-link-hover-bg:              transparent
    403 | // $navbar-inverse-link-active-color:          $navbar-inverse-link-hover-color
    404 | // $navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 10%)
    405 | // $navbar-inverse-link-disabled-color:        #444
    406 | // $navbar-inverse-link-disabled-bg:           transparent
    407 | 
    408 | // Inverted navbar brand label
    409 | // $navbar-inverse-brand-color:                $navbar-inverse-link-color
    410 | // $navbar-inverse-brand-hover-color:          #fff
    411 | // $navbar-inverse-brand-hover-bg:             transparent
    412 | 
    413 | // Inverted navbar toggle
    414 | // $navbar-inverse-toggle-hover-bg:            #333
    415 | // $navbar-inverse-toggle-icon-bar-bg:         #fff
    416 | // $navbar-inverse-toggle-border-color:        #333
    417 | 
    418 | 
    419 | //== Navs
    420 | //
    421 | //##
    422 | 
    423 | //=== Shared nav styles
    424 | // $nav-link-padding:                          10px 15px
    425 | // $nav-link-hover-bg:                         $gray-lighter
    426 | 
    427 | // $nav-disabled-link-color:                   $gray-light
    428 | // $nav-disabled-link-hover-color:             $gray-light
    429 | 
    430 | //== Tabs
    431 | // $nav-tabs-border-color:                     #ddd
    432 | 
    433 | // $nav-tabs-link-hover-border-color:          $gray-lighter
    434 | 
    435 | // $nav-tabs-active-link-hover-bg:             $body-bg
    436 | // $nav-tabs-active-link-hover-color:          $gray
    437 | // $nav-tabs-active-link-hover-border-color:   #ddd
    438 | 
    439 | // $nav-tabs-justified-link-border-color:            #ddd
    440 | // $nav-tabs-justified-active-link-border-color:     $body-bg
    441 | 
    442 | //== Pills
    443 | // $nav-pills-border-radius:                   $border-radius-base
    444 | // $nav-pills-active-link-hover-bg:            $component-active-bg
    445 | // $nav-pills-active-link-hover-color:         $component-active-color
    446 | 
    447 | 
    448 | //== Pagination
    449 | //
    450 | //##
    451 | 
    452 | // $pagination-color:                     $link-color
    453 | // $pagination-bg:                        #fff
    454 | // $pagination-border:                    #ddd
    455 | 
    456 | // $pagination-hover-color:               $link-hover-color
    457 | // $pagination-hover-bg:                  $gray-lighter
    458 | // $pagination-hover-border:              #ddd
    459 | 
    460 | // $pagination-active-color:              #fff
    461 | // $pagination-active-bg:                 $brand-primary
    462 | // $pagination-active-border:             $brand-primary
    463 | 
    464 | // $pagination-disabled-color:            $gray-light
    465 | // $pagination-disabled-bg:               #fff
    466 | // $pagination-disabled-border:           #ddd
    467 | 
    468 | 
    469 | //== Pager
    470 | //
    471 | //##
    472 | 
    473 | // $pager-bg:                             $pagination-bg
    474 | // $pager-border:                         $pagination-border
    475 | // $pager-border-radius:                  15px
    476 | 
    477 | // $pager-hover-bg:                       $pagination-hover-bg
    478 | 
    479 | // $pager-active-bg:                      $pagination-active-bg
    480 | // $pager-active-color:                   $pagination-active-color
    481 | 
    482 | // $pager-disabled-color:                 $pagination-disabled-color
    483 | 
    484 | 
    485 | //== Jumbotron
    486 | //
    487 | //##
    488 | 
    489 | // $jumbotron-padding:              30px
    490 | // $jumbotron-color:                inherit
    491 | // $jumbotron-bg:                   $gray-lighter
    492 | // $jumbotron-heading-color:        inherit
    493 | // $jumbotron-font-size:            ceil(($font-size-base * 1.5))
    494 | 
    495 | 
    496 | //== Form states and alerts
    497 | //
    498 | //## Define colors for form feedback states and, by default, alerts.
    499 | 
    500 | // $state-success-text:             #3c763d
    501 | // $state-success-bg:               #dff0d8
    502 | // $state-success-border:           darken(adjust-hue($state-success-bg, -10), 5%)
    503 | 
    504 | // $state-info-text:                #31708f
    505 | // $state-info-bg:                  #d9edf7
    506 | // $state-info-border:              darken(adjust-hue($state-info-bg, -10), 7%)
    507 | 
    508 | // $state-warning-text:             #8a6d3b
    509 | // $state-warning-bg:               #fcf8e3
    510 | // $state-warning-border:           darken(adjust-hue($state-warning-bg, -10), 5%)
    511 | 
    512 | // $state-danger-text:              #a94442
    513 | // $state-danger-bg:                #f2dede
    514 | // $state-danger-border:            darken(adjust-hue($state-danger-bg, -10), 5%)
    515 | 
    516 | 
    517 | //== Tooltips
    518 | //
    519 | //##
    520 | 
    521 | //** Tooltip max width
    522 | // $tooltip-max-width:           200px
    523 | //** Tooltip text color
    524 | // $tooltip-color:               #fff
    525 | //** Tooltip background color
    526 | // $tooltip-bg:                  #000
    527 | // $tooltip-opacity:             .9
    528 | 
    529 | //** Tooltip arrow width
    530 | // $tooltip-arrow-width:         5px
    531 | //** Tooltip arrow color
    532 | // $tooltip-arrow-color:         $tooltip-bg
    533 | 
    534 | 
    535 | //== Popovers
    536 | //
    537 | //##
    538 | 
    539 | //** Popover body background color
    540 | // $popover-bg:                          #fff
    541 | //** Popover maximum width
    542 | // $popover-max-width:                   276px
    543 | //** Popover border color
    544 | // $popover-border-color:                rgba(0,0,0,.2)
    545 | //** Popover fallback border color
    546 | // $popover-fallback-border-color:       #ccc
    547 | 
    548 | //** Popover title background color
    549 | // $popover-title-bg:                    darken($popover-bg, 3%)
    550 | 
    551 | //** Popover arrow width
    552 | // $popover-arrow-width:                 10px
    553 | //** Popover arrow color
    554 | // $popover-arrow-color:                 $popover-bg
    555 | 
    556 | //** Popover outer arrow width
    557 | // $popover-arrow-outer-width:           ($popover-arrow-width + 1)
    558 | //** Popover outer arrow color
    559 | // $popover-arrow-outer-color:           fade_in($popover-border-color, 0.05)
    560 | //** Popover outer arrow fallback color
    561 | // $popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%)
    562 | 
    563 | 
    564 | //== Labels
    565 | //
    566 | //##
    567 | 
    568 | //** Default label background color
    569 | // $label-default-bg:            $gray-light
    570 | //** Primary label background color
    571 | // $label-primary-bg:            $brand-primary
    572 | //** Success label background color
    573 | // $label-success-bg:            $brand-success
    574 | //** Info label background color
    575 | // $label-info-bg:               $brand-info
    576 | //** Warning label background color
    577 | // $label-warning-bg:            $brand-warning
    578 | //** Danger label background color
    579 | // $label-danger-bg:             $brand-danger
    580 | 
    581 | //** Default label text color
    582 | // $label-color:                 #fff
    583 | //** Default text color of a linked label
    584 | // $label-link-hover-color:      #fff
    585 | 
    586 | 
    587 | //== Modals
    588 | //
    589 | //##
    590 | 
    591 | //** Padding applied to the modal body
    592 | // $modal-inner-padding:         15px
    593 | 
    594 | //** Padding applied to the modal title
    595 | // $modal-title-padding:         15px
    596 | //** Modal title line-height
    597 | // $modal-title-line-height:     $line-height-base
    598 | 
    599 | //** Background color of modal content area
    600 | // $modal-content-bg:                             #fff
    601 | //** Modal content border color
    602 | // $modal-content-border-color:                   rgba(0,0,0,.2)
    603 | //** Modal content border color **for IE8**
    604 | // $modal-content-fallback-border-color:          #999
    605 | 
    606 | //** Modal backdrop background color
    607 | // $modal-backdrop-bg:           #000
    608 | //** Modal backdrop opacity
    609 | // $modal-backdrop-opacity:      .5
    610 | //** Modal header border color
    611 | // $modal-header-border-color:   #e5e5e5
    612 | //** Modal footer border color
    613 | // $modal-footer-border-color:   $modal-header-border-color
    614 | 
    615 | // $modal-lg:                    900px
    616 | // $modal-md:                    600px
    617 | // $modal-sm:                    300px
    618 | 
    619 | 
    620 | //== Alerts
    621 | //
    622 | //## Define alert colors, border radius, and padding.
    623 | 
    624 | // $alert-padding:               15px
    625 | // $alert-border-radius:         $border-radius-base
    626 | // $alert-link-font-weight:      bold
    627 | 
    628 | // $alert-success-bg:            $state-success-bg
    629 | // $alert-success-text:          $state-success-text
    630 | // $alert-success-border:        $state-success-border
    631 | 
    632 | // $alert-info-bg:               $state-info-bg
    633 | // $alert-info-text:             $state-info-text
    634 | // $alert-info-border:           $state-info-border
    635 | 
    636 | // $alert-warning-bg:            $state-warning-bg
    637 | // $alert-warning-text:          $state-warning-text
    638 | // $alert-warning-border:        $state-warning-border
    639 | 
    640 | // $alert-danger-bg:             $state-danger-bg
    641 | // $alert-danger-text:           $state-danger-text
    642 | // $alert-danger-border:         $state-danger-border
    643 | 
    644 | 
    645 | //== Progress bars
    646 | //
    647 | //##
    648 | 
    649 | //** Background color of the whole progress component
    650 | // $progress-bg:                 #f5f5f5
    651 | //** Progress bar text color
    652 | // $progress-bar-color:          #fff
    653 | //** Variable for setting rounded corners on progress bar.
    654 | // $progress-border-radius:      $border-radius-base
    655 | 
    656 | //** Default progress bar color
    657 | // $progress-bar-bg:             $brand-primary
    658 | //** Success progress bar color
    659 | // $progress-bar-success-bg:     $brand-success
    660 | //** Warning progress bar color
    661 | // $progress-bar-warning-bg:     $brand-warning
    662 | //** Danger progress bar color
    663 | // $progress-bar-danger-bg:      $brand-danger
    664 | //** Info progress bar color
    665 | // $progress-bar-info-bg:        $brand-info
    666 | 
    667 | 
    668 | //== List group
    669 | //
    670 | //##
    671 | 
    672 | //** Background color on `.list-group-item`
    673 | // $list-group-bg:                 #fff
    674 | //** `.list-group-item` border color
    675 | // $list-group-border:             #ddd
    676 | //** List group border radius
    677 | // $list-group-border-radius:      $border-radius-base
    678 | 
    679 | //** Background color of single list items on hover
    680 | // $list-group-hover-bg:           #f5f5f5
    681 | //** Text color of active list items
    682 | // $list-group-active-color:       $component-active-color
    683 | //** Background color of active list items
    684 | // $list-group-active-bg:          $component-active-bg
    685 | //** Border color of active list elements
    686 | // $list-group-active-border:      $list-group-active-bg
    687 | //** Text color for content within active list items
    688 | // $list-group-active-text-color:  lighten($list-group-active-bg, 40%)
    689 | 
    690 | //** Text color of disabled list items
    691 | // $list-group-disabled-color:      $gray-light
    692 | //** Background color of disabled list items
    693 | // $list-group-disabled-bg:         $gray-lighter
    694 | //** Text color for content within disabled list items
    695 | // $list-group-disabled-text-color: $list-group-disabled-color
    696 | 
    697 | // $list-group-link-color:         #555
    698 | // $list-group-link-hover-color:   $list-group-link-color
    699 | // $list-group-link-heading-color: #333
    700 | 
    701 | 
    702 | //== Panels
    703 | //
    704 | //##
    705 | 
    706 | // $panel-bg:                    #fff
    707 | // $panel-body-padding:          15px
    708 | // $panel-heading-padding:       10px 15px
    709 | // $panel-footer-padding:        $panel-heading-padding
    710 | // $panel-border-radius:         $border-radius-base
    711 | 
    712 | //** Border color for elements within panels
    713 | // $panel-inner-border:          #ddd
    714 | // $panel-footer-bg:             #f5f5f5
    715 | 
    716 | // $panel-default-text:          $gray-dark
    717 | // $panel-default-border:        #ddd
    718 | // $panel-default-heading-bg:    #f5f5f5
    719 | 
    720 | // $panel-primary-text:          #fff
    721 | // $panel-primary-border:        $brand-primary
    722 | // $panel-primary-heading-bg:    $brand-primary
    723 | 
    724 | // $panel-success-text:          $state-success-text
    725 | // $panel-success-border:        $state-success-border
    726 | // $panel-success-heading-bg:    $state-success-bg
    727 | 
    728 | // $panel-info-text:             $state-info-text
    729 | // $panel-info-border:           $state-info-border
    730 | // $panel-info-heading-bg:       $state-info-bg
    731 | 
    732 | // $panel-warning-text:          $state-warning-text
    733 | // $panel-warning-border:        $state-warning-border
    734 | // $panel-warning-heading-bg:    $state-warning-bg
    735 | 
    736 | // $panel-danger-text:           $state-danger-text
    737 | // $panel-danger-border:         $state-danger-border
    738 | // $panel-danger-heading-bg:     $state-danger-bg
    739 | 
    740 | 
    741 | //== Thumbnails
    742 | //
    743 | //##
    744 | 
    745 | //** Padding around the thumbnail image
    746 | // $thumbnail-padding:           4px
    747 | //** Thumbnail background color
    748 | // $thumbnail-bg:                $body-bg
    749 | //** Thumbnail border color
    750 | // $thumbnail-border:            #ddd
    751 | //** Thumbnail border radius
    752 | // $thumbnail-border-radius:     $border-radius-base
    753 | 
    754 | //** Custom text color for thumbnail captions
    755 | // $thumbnail-caption-color:     $text-color
    756 | //** Padding around the thumbnail caption
    757 | // $thumbnail-caption-padding:   9px
    758 | 
    759 | 
    760 | //== Wells
    761 | //
    762 | //##
    763 | 
    764 | // $well-bg:                     #f5f5f5
    765 | // $well-border:                 darken($well-bg, 7%)
    766 | 
    767 | 
    768 | //== Badges
    769 | //
    770 | //##
    771 | 
    772 | // $badge-color:                 #fff
    773 | //** Linked badge text color on hover
    774 | // $badge-link-hover-color:      #fff
    775 | // $badge-bg:                    $gray-light
    776 | 
    777 | //** Badge text color in active nav link
    778 | // $badge-active-color:          $link-color
    779 | //** Badge background color in active nav link
    780 | // $badge-active-bg:             #fff
    781 | 
    782 | // $badge-font-weight:           bold
    783 | // $badge-line-height:           1
    784 | // $badge-border-radius:         10px
    785 | 
    786 | 
    787 | //== Breadcrumbs
    788 | //
    789 | //##
    790 | 
    791 | // $breadcrumb-padding-vertical:   8px
    792 | // $breadcrumb-padding-horizontal: 15px
    793 | //** Breadcrumb background color
    794 | // $breadcrumb-bg:                 #f5f5f5
    795 | //** Breadcrumb text color
    796 | // $breadcrumb-color:              #ccc
    797 | //** Text color of current page in the breadcrumb
    798 | // $breadcrumb-active-color:       $gray-light
    799 | //** Textual separator for between breadcrumb elements
    800 | // $breadcrumb-separator:          "/"
    801 | 
    802 | 
    803 | //== Carousel
    804 | //
    805 | //##
    806 | 
    807 | // $carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6)
    808 | 
    809 | // $carousel-control-color:                      #fff
    810 | // $carousel-control-width:                      15%
    811 | // $carousel-control-opacity:                    .5
    812 | // $carousel-control-font-size:                  20px
    813 | 
    814 | // $carousel-indicator-active-bg:                #fff
    815 | // $carousel-indicator-border-color:             #fff
    816 | 
    817 | // $carousel-caption-color:                      #fff
    818 | 
    819 | 
    820 | //== Close
    821 | //
    822 | //##
    823 | 
    824 | // $close-font-weight:           bold
    825 | // $close-color:                 #000
    826 | // $close-text-shadow:           0 1px 0 #fff
    827 | 
    828 | 
    829 | //== Code
    830 | //
    831 | //##
    832 | 
    833 | // $code-color:                  #c7254e
    834 | // $code-bg:                     #f9f2f4
    835 | 
    836 | // $kbd-color:                   #fff
    837 | // $kbd-bg:                      #333
    838 | 
    839 | // $pre-bg:                      #f5f5f5
    840 | // $pre-color:                   $gray-dark
    841 | // $pre-border-color:            #ccc
    842 | // $pre-scrollable-max-height:   340px
    843 | 
    844 | 
    845 | //== Type
    846 | //
    847 | //##
    848 | 
    849 | //** Horizontal offset for forms and lists.
    850 | // $component-offset-horizontal: 180px
    851 | //** Text muted color
    852 | // $text-muted:                  $gray-light
    853 | //** Abbreviations and acronyms border color
    854 | // $abbr-border-color:           $gray-light
    855 | //** Headings small color
    856 | // $headings-small-color:        $gray-light
    857 | //** Blockquote small color
    858 | // $blockquote-small-color:      $gray-light
    859 | //** Blockquote font size
    860 | // $blockquote-font-size:        ($font-size-base * 1.25)
    861 | //** Blockquote border color
    862 | // $blockquote-border-color:     $gray-lighter
    863 | //** Page header border color
    864 | // $page-header-border-color:    $gray-lighter
    865 | //** Width of horizontal description list titles
    866 | // $dl-horizontal-offset:        $component-offset-horizontal
    867 | //** Horizontal line color.
    868 | // $hr-border:                   $gray-lighter
    869 | $nprogress-color: #5BC0DE;
    870 | 
    
    
    --------------------------------------------------------------------------------