Compare commits
No commits in common. "master" and "rails4" have entirely different histories.
|
@ -8,13 +8,8 @@
|
|||
*.swp
|
||||
|
||||
/public/uploads/*
|
||||
/coverage
|
||||
|
||||
/config/application.yml
|
||||
/config/mongoid.yml
|
||||
/config/database.yml
|
||||
/public/assets/*
|
||||
*.old
|
||||
*.bak
|
||||
.byebug_history
|
||||
.ruby-version
|
||||
|
|
26
.travis.yml
|
@ -1,9 +1,10 @@
|
|||
# http://about.travis-ci.org/docs/user/build-configuration/
|
||||
env:
|
||||
global:
|
||||
- CC_TEST_REPORTER_ID=787a2f89b15c637323c7340d65ec17e898ac44480706b4b4122ea040c2a88f1d
|
||||
language: ruby
|
||||
|
||||
#bundler_args: '--without production'
|
||||
|
||||
script: CODECLIMATE_REPO_TOKEN=7d6b988f58234bf1560305412d10d9530902beebdbe2fb938b6a934f1d352578 bundle exec rake
|
||||
|
||||
before_install:
|
||||
- "cat /etc/timezone"
|
||||
- "grep -i processor /proc/cpuinfo | wc -l"
|
||||
|
@ -12,20 +13,11 @@ before_install:
|
|||
- "bundle -v"
|
||||
|
||||
before_script:
|
||||
- "curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter"
|
||||
- "chmod +x ./cc-test-reporter"
|
||||
- "./cc-test-reporter before-build"
|
||||
- "cp -f config/database.yml.example config/database.yml"
|
||||
- "cp -f config/application.yml.example config/application.yml"
|
||||
- "bundle exec rake db:drop db:create db:schema:load --trace 2>&1"
|
||||
|
||||
script: bundle exec rspec
|
||||
|
||||
after_script:
|
||||
- "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
|
||||
|
||||
- "bundle exec rake travis:before_script"
|
||||
services:
|
||||
- postgresql
|
||||
- mongodb
|
||||
|
||||
rvm:
|
||||
- 2.5.3
|
||||
- 2.1.0
|
||||
- 2.2.0
|
||||
- 2.3.0
|
||||
|
|
60
Gemfile
|
@ -1,73 +1,63 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
ruby '2.5.3'
|
||||
|
||||
gem 'rails', '~> 5.2.0'
|
||||
gem 'sass-rails', '~> 5.0'
|
||||
gem 'coffee-rails', '~> 4.2'
|
||||
gem 'rails', '4.2.5.2'
|
||||
gem 'sass-rails'
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
gem 'uglifier', '>= 2.7.2'
|
||||
|
||||
gem 'jquery-rails'
|
||||
gem 'foundation-rails', '~> 6.2.1'
|
||||
gem 'foundation-rails', '~> 5.5.1'
|
||||
gem 'foundation-icons-sass-rails'
|
||||
gem 'font-awesome-sass', '4.7.0'
|
||||
gem 'carrierwave'
|
||||
gem 'kaminari'
|
||||
gem 'turbolinks', '~> 5.x'
|
||||
gem 'js_cookie_rails'
|
||||
|
||||
gem 'rails-i18n', '~> 5.1'
|
||||
gem 'font-awesome-sass'
|
||||
|
||||
gem 'jbuilder'
|
||||
gem 'pg', '~> 0.18'
|
||||
|
||||
gem 'mongoid'
|
||||
gem 'mongoid-tree'
|
||||
gem 'mongoid-pagination'
|
||||
gem 'redcarpet'
|
||||
gem 'rouge'
|
||||
gem 'slim-rails'
|
||||
gem 'simple_form', '~> 4.0.0'
|
||||
gem 'simple_form'
|
||||
gem 'mini_magick'
|
||||
gem 'carrierwave-mongoid'
|
||||
gem 'html_truncator'
|
||||
gem 'nokogiri'
|
||||
gem 'angularjs-rails'
|
||||
gem 'figaro'
|
||||
gem 'rqrcode-with-patches', require: 'rqrcode'
|
||||
gem 'chunky_png'
|
||||
gem 'sidekiq'
|
||||
gem 'redis-namespace'
|
||||
gem 'rest-client'
|
||||
gem 'unicorn'
|
||||
gem 'newrelic_rpm'
|
||||
|
||||
gem 'puma'
|
||||
gem 'bootsnap', '>= 1.3.0', require: false
|
||||
|
||||
gem 'mina', '~> 1.2.2', require: false
|
||||
gem 'mina-ng-puma', '~> 1.3.0', require: false
|
||||
gem 'mina-multistage', '~> 1.0.3', require: false
|
||||
gem 'mina-sidekiq', '~> 1.0.3', require: false
|
||||
gem 'mina-logs', '~> 1.1.0', require: false
|
||||
|
||||
gem 'browser_warrior'
|
||||
gem 'mina', require: false
|
||||
gem 'mina-multistage', require: false
|
||||
gem 'mina-sidekiq', require: false
|
||||
gem 'mina-unicorn', require: false
|
||||
|
||||
group :development do
|
||||
gem 'spring'
|
||||
gem 'quiet_assets'
|
||||
gem 'guard'
|
||||
gem 'guard-rails'
|
||||
gem 'guard-rspec', require: false
|
||||
gem 'guard-bundler', require: false
|
||||
gem 'listen', '~> 3.x'
|
||||
gem 'spring'
|
||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||
|
||||
gem 'rack-cors', :require => 'rack/cors'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara'
|
||||
gem 'mongoid-rspec', :require => false
|
||||
gem 'database_cleaner'
|
||||
gem 'rspec-sidekiq'
|
||||
gem "codeclimate-test-reporter", group: :test, require: nil
|
||||
gem 'simplecov'
|
||||
gem 'simplecov-console'
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
gem "rspec-rails", ">= 2.8.1"
|
||||
gem 'byebug'
|
||||
gem 'factory_bot_rails'
|
||||
gem 'rails-controller-testing'
|
||||
gem 'pry-rails'
|
||||
gem 'pry-nav'
|
||||
gem 'factory_girl_rails'
|
||||
end
|
||||
|
|
589
Gemfile.lock
|
@ -1,377 +1,338 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activejob (= 5.2.3)
|
||||
actionmailer (4.2.5.2)
|
||||
actionpack (= 4.2.5.2)
|
||||
actionview (= 4.2.5.2)
|
||||
activejob (= 4.2.5.2)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
rack (~> 2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (4.2.5.2)
|
||||
actionview (= 4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
actionview (4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activejob (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activerecord (5.2.3)
|
||||
activemodel (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
arel (>= 9.0)
|
||||
activestorage (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
activerecord (= 5.2.3)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.3)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
activejob (4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.5.2)
|
||||
activemodel (= 4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.5.2)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.6.0)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
ansi (1.5.0)
|
||||
arel (9.0.0)
|
||||
babel-source (5.8.35)
|
||||
babel-transpiler (0.7.0)
|
||||
babel-source (>= 4.0, < 6)
|
||||
execjs (~> 2.0)
|
||||
bootsnap (1.4.3)
|
||||
msgpack (~> 1.0)
|
||||
browser (2.5.3)
|
||||
browser_warrior (0.10.0)
|
||||
browser
|
||||
rails (~> 5.0)
|
||||
sass-rails (~> 5.0)
|
||||
builder (3.2.3)
|
||||
byebug (11.0.1)
|
||||
capybara (3.17.0)
|
||||
addressable (2.4.0)
|
||||
angularjs-rails (1.5.0)
|
||||
arel (6.0.3)
|
||||
bson (4.0.4)
|
||||
builder (3.2.2)
|
||||
capybara (2.6.2)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (~> 1.2)
|
||||
xpath (~> 3.2)
|
||||
carrierwave (1.3.1)
|
||||
activemodel (>= 4.0.0)
|
||||
activesupport (>= 4.0.0)
|
||||
mime-types (>= 1.16)
|
||||
codeclimate-test-reporter (1.0.7)
|
||||
simplecov
|
||||
coderay (1.1.2)
|
||||
coffee-rails (4.2.2)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
carrierwave (0.10.0)
|
||||
activemodel (>= 3.2.0)
|
||||
activesupport (>= 3.2.0)
|
||||
json (>= 1.7)
|
||||
mime-types (>= 1.16)
|
||||
carrierwave-mongoid (0.8.1)
|
||||
carrierwave (>= 0.8.0, < 0.11.0)
|
||||
mongoid (>= 3.0, < 6.0)
|
||||
mongoid-grid_fs (>= 1.3, < 3.0)
|
||||
chunky_png (1.3.5)
|
||||
codeclimate-test-reporter (0.5.0)
|
||||
simplecov (>= 0.7.1, < 1.0.0)
|
||||
coderay (1.1.1)
|
||||
coffee-rails (4.1.1)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0)
|
||||
railties (>= 4.0.0, < 5.1.x)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
concurrent-ruby (1.1.5)
|
||||
connection_pool (2.2.2)
|
||||
crass (1.0.4)
|
||||
database_cleaner (1.7.0)
|
||||
diff-lcs (1.3)
|
||||
docile (1.3.1)
|
||||
domain_name (0.5.20180417)
|
||||
coffee-script-source (1.10.0)
|
||||
concurrent-ruby (1.0.1)
|
||||
connection_pool (2.2.0)
|
||||
database_cleaner (1.5.1)
|
||||
diff-lcs (1.2.5)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.20160309)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
erubi (1.8.0)
|
||||
execjs (2.7.0)
|
||||
factory_bot (5.0.2)
|
||||
activesupport (>= 4.2.0)
|
||||
factory_bot_rails (5.0.2)
|
||||
factory_bot (~> 5.0.2)
|
||||
railties (>= 4.2.0)
|
||||
ffi (1.10.0)
|
||||
erubis (2.7.0)
|
||||
execjs (2.6.0)
|
||||
factory_girl (4.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.6.0)
|
||||
factory_girl (~> 4.5.0)
|
||||
railties (>= 3.0.0)
|
||||
ffi (1.9.10)
|
||||
figaro (1.1.1)
|
||||
thor (~> 0.14)
|
||||
font-awesome-sass (4.7.0)
|
||||
font-awesome-sass (4.5.0)
|
||||
sass (>= 3.2)
|
||||
formatador (0.2.5)
|
||||
foundation-icons-sass-rails (3.0.0)
|
||||
railties (>= 3.1.1)
|
||||
sass-rails (>= 3.1.1)
|
||||
foundation-rails (6.2.4.0)
|
||||
foundation-rails (5.5.3.2)
|
||||
railties (>= 3.1.0)
|
||||
sass (>= 3.3.0, < 3.5)
|
||||
sprockets-es6 (>= 0.9.0)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
guard (2.15.0)
|
||||
globalid (0.3.6)
|
||||
activesupport (>= 4.1.0)
|
||||
guard (2.13.0)
|
||||
formatador (>= 0.2.4)
|
||||
listen (>= 2.7, < 4.0)
|
||||
lumberjack (>= 1.0.12, < 2.0)
|
||||
listen (>= 2.7, <= 4.0)
|
||||
lumberjack (~> 1.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-bundler (2.2.1)
|
||||
bundler (>= 1.3.0, < 3)
|
||||
guard-bundler (2.1.0)
|
||||
bundler (~> 1.0)
|
||||
guard (~> 2.2)
|
||||
guard-compat (~> 1.1)
|
||||
guard-compat (1.2.1)
|
||||
guard-rails (0.8.1)
|
||||
guard-rails (0.7.2)
|
||||
guard (~> 2.11)
|
||||
guard-compat (~> 1.0)
|
||||
guard-rspec (4.7.3)
|
||||
guard-rspec (4.6.4)
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
hirb (0.7.3)
|
||||
html_truncator (0.4.2)
|
||||
html_truncator (0.4.1)
|
||||
nokogiri (~> 1.5)
|
||||
http-cookie (1.0.3)
|
||||
http-cookie (1.0.2)
|
||||
domain_name (~> 0.5)
|
||||
i18n (1.6.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jbuilder (2.8.0)
|
||||
activesupport (>= 4.2.0)
|
||||
multi_json (>= 1.2)
|
||||
jquery-rails (4.3.3)
|
||||
i18n (0.7.0)
|
||||
jbuilder (2.4.1)
|
||||
activesupport (>= 3.0.0, < 5.1)
|
||||
multi_json (~> 1.2)
|
||||
jquery-rails (4.1.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
js_cookie_rails (2.2.0)
|
||||
railties (>= 3.1)
|
||||
json (2.2.0)
|
||||
kaminari (1.1.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.1.1)
|
||||
kaminari-activerecord (= 1.1.1)
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-actionview (1.1.1)
|
||||
actionview
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-activerecord (1.1.1)
|
||||
activerecord
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-core (1.1.1)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
loofah (2.2.3)
|
||||
crass (~> 1.0.2)
|
||||
json (1.8.3)
|
||||
kgio (2.10.0)
|
||||
listen (3.0.6)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9.7)
|
||||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
lumberjack (1.0.13)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (0.3.3)
|
||||
mimemagic (~> 0.3.2)
|
||||
method_source (0.9.2)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2019.0331)
|
||||
mimemagic (0.3.3)
|
||||
mina (1.2.3)
|
||||
lumberjack (1.0.10)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.99.1)
|
||||
mina (0.3.8)
|
||||
open4 (~> 1.3.4)
|
||||
rake
|
||||
mina-logs (1.1.0)
|
||||
mina (>= 1.0.2)
|
||||
mina-multistage (1.0.3)
|
||||
mina (~> 1.0)
|
||||
mina-ng-puma (1.3.0)
|
||||
mina (~> 1.2.0)
|
||||
puma (>= 2.13)
|
||||
mina-sidekiq (1.0.3)
|
||||
mina (>= 1.0.2)
|
||||
mini_magick (4.9.3)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.11.3)
|
||||
msgpack (1.2.10)
|
||||
multi_json (1.13.1)
|
||||
mina-multistage (1.0.2)
|
||||
mina (>= 0.2.1)
|
||||
mina-sidekiq (0.3.1)
|
||||
mina
|
||||
mina-unicorn (0.4.0)
|
||||
mini_magick (4.4.0)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.4)
|
||||
mongo (2.2.4)
|
||||
bson (~> 4.0)
|
||||
mongoid (5.1.1)
|
||||
activemodel (~> 4.0)
|
||||
mongo (~> 2.1)
|
||||
origin (~> 2.2)
|
||||
tzinfo (>= 0.3.37)
|
||||
mongoid-grid_fs (2.2.1)
|
||||
mime-types (>= 1.0, < 3.0)
|
||||
mongoid (>= 3.0, < 6.0)
|
||||
mongoid-pagination (0.2.0)
|
||||
activesupport
|
||||
mongoid
|
||||
mongoid-rspec (3.0.0)
|
||||
mongoid (~> 5.0)
|
||||
rake
|
||||
rspec (~> 3.3)
|
||||
mongoid-tree (2.0.1)
|
||||
mongoid (>= 4.0, < 6.0)
|
||||
multi_json (1.11.2)
|
||||
nenv (0.3.0)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.3.1)
|
||||
nokogiri (1.10.2)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
notiffany (0.1.1)
|
||||
newrelic_rpm (3.15.0.314)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
notiffany (0.0.8)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
open4 (1.3.4)
|
||||
pg (0.21.0)
|
||||
pry (0.12.2)
|
||||
origin (2.2.0)
|
||||
pry (0.10.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
public_suffix (3.0.3)
|
||||
puma (3.12.1)
|
||||
rack (2.0.7)
|
||||
rack-cors (1.0.3)
|
||||
rack-protection (2.0.5)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.3)
|
||||
actioncable (= 5.2.3)
|
||||
actionmailer (= 5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activejob (= 5.2.3)
|
||||
activemodel (= 5.2.3)
|
||||
activerecord (= 5.2.3)
|
||||
activestorage (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.3)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
actionview (>= 5.0.1.x)
|
||||
activesupport (>= 5.0.1.x)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
rails-i18n (5.1.3)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 5.0, < 6)
|
||||
railties (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
method_source
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
pry-nav (0.2.4)
|
||||
pry (>= 0.9.10, < 0.11.0)
|
||||
pry-rails (0.3.4)
|
||||
pry (>= 0.9.10)
|
||||
quiet_assets (1.1.0)
|
||||
railties (>= 3.1, < 5.0)
|
||||
rack (1.6.4)
|
||||
rack-cors (0.4.0)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.5.2)
|
||||
actionmailer (= 4.2.5.2)
|
||||
actionpack (= 4.2.5.2)
|
||||
actionview (= 4.2.5.2)
|
||||
activejob (= 4.2.5.2)
|
||||
activemodel (= 4.2.5.2)
|
||||
activerecord (= 4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.5.2)
|
||||
sprockets-rails
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.7)
|
||||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
railties (4.2.5.2)
|
||||
actionpack (= 4.2.5.2)
|
||||
activesupport (= 4.2.5.2)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rake (12.3.2)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
ffi (~> 1.0)
|
||||
redcarpet (3.4.0)
|
||||
redis (4.1.0)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
regexp_parser (1.4.0)
|
||||
rest-client (2.0.2)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
raindrops (0.16.0)
|
||||
rake (11.1.1)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
redcarpet (3.3.4)
|
||||
redis (3.2.2)
|
||||
redis-namespace (1.5.2)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
rest-client (1.8.0)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 4.0)
|
||||
netrc (~> 0.8)
|
||||
rouge (3.3.0)
|
||||
rspec (3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-core (3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.3)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rouge (1.10.1)
|
||||
rqrcode-with-patches (0.6.0)
|
||||
chunky_png
|
||||
rspec (3.4.0)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-core (3.4.4)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-expectations (3.4.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-mocks (3.4.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-rails (3.8.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-sidekiq (3.0.3)
|
||||
rspec-core (~> 3.0, >= 3.0.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-rails (3.4.2)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-sidekiq (2.2.0)
|
||||
rspec (~> 3.0, >= 3.0.0)
|
||||
sidekiq (>= 2.4.0)
|
||||
rspec-support (3.8.0)
|
||||
ruby_dep (1.5.0)
|
||||
sass (3.4.25)
|
||||
sass-rails (5.0.7)
|
||||
railties (>= 4.0.0, < 6)
|
||||
rspec-support (3.4.1)
|
||||
sass (3.4.21)
|
||||
sass-rails (5.0.4)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
shellany (0.0.1)
|
||||
sidekiq (5.2.6)
|
||||
connection_pool (~> 2.2, >= 2.2.2)
|
||||
rack (>= 1.5.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (>= 3.3.5, < 5)
|
||||
simple_form (4.0.1)
|
||||
actionpack (>= 5.0)
|
||||
activemodel (>= 5.0)
|
||||
simplecov (0.16.1)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
sidekiq (4.1.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
simple_form (3.2.1)
|
||||
actionpack (> 4, < 5.1)
|
||||
activemodel (> 4, < 5.1)
|
||||
simplecov (0.11.2)
|
||||
docile (~> 1.1.0)
|
||||
json (~> 1.8)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-console (0.4.2)
|
||||
ansi
|
||||
hirb
|
||||
simplecov
|
||||
simplecov-html (0.10.2)
|
||||
slim (4.0.1)
|
||||
temple (>= 0.7.6, < 0.9)
|
||||
tilt (>= 2.0.6, < 2.1)
|
||||
slim-rails (3.2.0)
|
||||
actionpack (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
slim (>= 3.0, < 5.0)
|
||||
spring (2.0.2)
|
||||
activesupport (>= 4.2)
|
||||
spring-watcher-listen (2.0.1)
|
||||
listen (>= 2.7, < 4.0)
|
||||
spring (>= 1.2, < 3.0)
|
||||
sprockets (3.7.2)
|
||||
simplecov-html (0.10.0)
|
||||
slim (3.0.6)
|
||||
temple (~> 0.7.3)
|
||||
tilt (>= 1.3.3, < 2.1)
|
||||
slim-rails (3.0.1)
|
||||
actionmailer (>= 3.1, < 5.0)
|
||||
actionpack (>= 3.1, < 5.0)
|
||||
activesupport (>= 3.1, < 5.0)
|
||||
railties (>= 3.1, < 5.0)
|
||||
slim (~> 3.0)
|
||||
slop (3.6.0)
|
||||
spring (1.6.4)
|
||||
sprockets (3.5.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-es6 (0.9.2)
|
||||
babel-source (>= 5.8.11)
|
||||
babel-transpiler
|
||||
sprockets (>= 3.0.0)
|
||||
sprockets-rails (3.2.1)
|
||||
sprockets-rails (3.0.4)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
temple (0.8.1)
|
||||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
turbolinks (5.2.0)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (1.2.5)
|
||||
temple (0.7.6)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
tilt (2.0.2)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (4.1.20)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.6)
|
||||
websocket-driver (0.7.0)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.3)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
unf_ext (0.0.7.2)
|
||||
unicorn (5.0.1)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
bootsnap (>= 1.3.0)
|
||||
browser_warrior
|
||||
byebug
|
||||
angularjs-rails
|
||||
capybara
|
||||
carrierwave
|
||||
carrierwave-mongoid
|
||||
chunky_png
|
||||
codeclimate-test-reporter
|
||||
coffee-rails (~> 4.2)
|
||||
coffee-rails (~> 4.1.0)
|
||||
database_cleaner
|
||||
factory_bot_rails
|
||||
factory_girl_rails
|
||||
figaro
|
||||
font-awesome-sass (= 4.7.0)
|
||||
font-awesome-sass
|
||||
foundation-icons-sass-rails
|
||||
foundation-rails (~> 6.2.1)
|
||||
foundation-rails (~> 5.5.1)
|
||||
guard
|
||||
guard-bundler
|
||||
guard-rails
|
||||
|
@ -379,41 +340,33 @@ DEPENDENCIES
|
|||
html_truncator
|
||||
jbuilder
|
||||
jquery-rails
|
||||
js_cookie_rails
|
||||
kaminari
|
||||
listen (~> 3.x)
|
||||
mina (~> 1.2.2)
|
||||
mina-logs (~> 1.1.0)
|
||||
mina-multistage (~> 1.0.3)
|
||||
mina-ng-puma (~> 1.3.0)
|
||||
mina-sidekiq (~> 1.0.3)
|
||||
mina
|
||||
mina-multistage
|
||||
mina-sidekiq
|
||||
mina-unicorn
|
||||
mini_magick
|
||||
mongoid
|
||||
mongoid-pagination
|
||||
mongoid-rspec
|
||||
mongoid-tree
|
||||
newrelic_rpm
|
||||
nokogiri
|
||||
pg (~> 0.18)
|
||||
puma
|
||||
pry-nav
|
||||
pry-rails
|
||||
quiet_assets
|
||||
rack-cors
|
||||
rails (~> 5.2.0)
|
||||
rails-controller-testing
|
||||
rails-i18n (~> 5.1)
|
||||
rails (= 4.2.5.2)
|
||||
redcarpet
|
||||
redis-namespace
|
||||
rest-client
|
||||
rouge
|
||||
rqrcode-with-patches
|
||||
rspec-rails (>= 2.8.1)
|
||||
rspec-sidekiq
|
||||
sass-rails (~> 5.0)
|
||||
sass-rails
|
||||
sidekiq
|
||||
simple_form (~> 4.0.0)
|
||||
simplecov
|
||||
simplecov-console
|
||||
simple_form
|
||||
slim-rails
|
||||
spring
|
||||
spring-watcher-listen (~> 2.0.0)
|
||||
turbolinks (~> 5.x)
|
||||
uglifier (>= 2.7.2)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.5.3p105
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.1
|
||||
unicorn
|
||||
|
|
3
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) <2012-2016> <liyafei>
|
||||
Copyright (c) <2012-2014> <liyafei>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
|
121
README.md
|
@ -1,43 +1,38 @@
|
|||
WBlog
|
||||
=======
|
||||
[](https://travis-ci.org/windy/wblog)
|
||||
[](https://codeclimate.com/github/windy/wblog/maintainability)
|
||||
[](https://codeclimate.com/github/windy/wblog/test_coverage)
|
||||
[](https://codeclimate.com/github/windy/wblog)
|
||||
[](https://codeclimate.com/github/windy/wblog)
|
||||
|
||||
The missing open source blog system on Ruby on Rails.
|
||||
|
||||
WBlog is open source blog which built for mobile first, it's licenced on MIT, use it for free!
|
||||
|
||||
New: WBlog is using Ruby on Rails 5.2 now.
|
||||
|
||||
[中文说明文档](/README.zh-CN.md)
|
||||
|
||||
Characteristic:
|
||||
|
||||
* Modern clean reading feelings
|
||||
* Markdown support, give nice formatted articles
|
||||
* Mobile first, responsive page for iPhone, iPad, iMac.
|
||||
* Independent comment system, subscribe system, picture manage system
|
||||
* Awful reading feeling for reader
|
||||
* Independent comment system, store data on your own sever
|
||||
* With mardown supported, you can post both powerful and clean formatted article(s)
|
||||
|
||||
A demo came from my English blog: <http://en.yafeilee.me>
|
||||
|
||||
Another demo using my blog: <http://yafeilee.me>
|
||||
|
||||
Power Admin Dashboard: <http://en.yafeilee.me/admin>, user and password are configurable.
|
||||
|
||||
Some [screenshots](#screenshots)
|
||||

|
||||
|
||||
### System dependencies
|
||||
|
||||
* Ruby ( = 2.3.1 )
|
||||
* Postgresql ( >= 9.x )
|
||||
* Ruby ( >= 2.0 )
|
||||
* Mongodb ( >= 2.7 )
|
||||
* Nginx ( >= 1.4 )
|
||||
|
||||
### Features
|
||||
|
||||
* Responsive, iPhone, iPad, Notebook, PC, all are supported
|
||||
* QR Code, Like button make your article easily sharing with your friends
|
||||
* Inpendent comment system, subscribe system, that all belong to you
|
||||
* QR Code attached article, scan and share it
|
||||
* Inpendent comment system, managed by yourself
|
||||
* Markdown supported, code highlight, especially for programmer, like you
|
||||
* Personalize it, commercialize it, it all depends on you
|
||||
|
||||
|
@ -45,37 +40,31 @@ Some [screenshots](#screenshots)
|
|||
|
||||
Make it to the best Ruby on Rails Blog system in the world.
|
||||
|
||||
### Running in development mode
|
||||
### Study it locally
|
||||
|
||||
WBlog MUST run in Linux or OSX. I assume you are using OS X 10.
|
||||
|
||||
You can run it like a Ruby on Rails project as usual:
|
||||
|
||||
0. Check dependencies
|
||||
|
||||
```shell
|
||||
ruby -v
|
||||
# 2.3.1
|
||||
postgres --version
|
||||
# 9.x.x
|
||||
```
|
||||
WBlog MUST run in Linux or Mac, it depends on Mongodb database. You can run it like a Ruby on Rails project as usual:
|
||||
|
||||
1. Clone it
|
||||
|
||||
`git clone git@github.com:windy/wblog.git`
|
||||
|
||||
`cd wblog`
|
||||
|
||||
`cd wblog `
|
||||
|
||||
2. Install dependencies & configure
|
||||
|
||||
```shell
|
||||
gem install bundler
|
||||
bundle install
|
||||
cp config/application.yml.example config/application.yml
|
||||
cp config/database.yml.example config/database.yml
|
||||
# Install mongodb ( see how to install it on your platform )
|
||||
# on Mac, you can install it like this:
|
||||
brew install mongodb
|
||||
```
|
||||
|
||||
Update `application.yml` & `database.yml` 's content as you need
|
||||
```shell
|
||||
bundle install
|
||||
cp config/application.yml.example config/application.yml
|
||||
cp config/mongoid.yml.example config/mongoid.yml
|
||||
```
|
||||
|
||||
Update application.yml & mongoid.yml as you need
|
||||
|
||||
3. Start it
|
||||
|
||||
|
@ -83,39 +72,35 @@ You can run it like a Ruby on Rails project as usual:
|
|||
rails s
|
||||
```
|
||||
|
||||
Open browser with `http://localhost:3000`
|
||||
|
||||
If there is any error found, please check your database's user and password.
|
||||
|
||||
4. Post the first blog
|
||||
4. Post the first article
|
||||
|
||||
visit: http://localhost:3000/admin, input your username and password configurated in `application.yml`.
|
||||
visit: http://127.0.0.1:3000/admin, input your username and password that is just configurated in application.yml.
|
||||
then, post a new article.
|
||||
|
||||
OK, That's all.
|
||||
|
||||
### Deployment
|
||||
|
||||
WBlog uses `mina` as automation deployment tool, uses `puma` as the Rack container.
|
||||
WBlog uses `mina` as automation deployment tool, uses `unicorn` as the Rack container.
|
||||
|
||||
WBlog recommends `nginx` as reverse proxy server.
|
||||
|
||||
It will be very fast.
|
||||
|
||||
Ruby on Rails project deployment is another topic, I would NOT talk it here.
|
||||
Ruby on Rails project deployment is another big topic, I would NOT talk it here.
|
||||
|
||||
You can read WBlog wiki for more information: [WBlog 的发布流程(Chinese only now)](https://github.com/windy/wblog/wiki)
|
||||
|
||||
### Stack
|
||||
|
||||
* Ruby on Rails 5.2.0
|
||||
* Ruby 2.3.1
|
||||
* Turbolinks 5 / SJR
|
||||
* Foundation 6
|
||||
* Ruby on Rails 4.2.x / Ruby 2.x
|
||||
* AngularJS
|
||||
* Foundation 5
|
||||
* mina
|
||||
* slim
|
||||
* puma
|
||||
* Postgresql
|
||||
* Mongodb
|
||||
|
||||
|
||||
## Related open source blog systems
|
||||
|
@ -125,45 +110,3 @@ You can read WBlog wiki for more information: [WBlog 的发布流程(Chinese onl
|
|||
* octopress( Github Pages ): <http://octopress.org/>
|
||||
* middleman( Ruby Gem ): Another static blog system <https://github.com/middleman/middleman>
|
||||
* robbin_site( Padrino ): <https://github.com/robbin/robbin_site>
|
||||
|
||||
## License
|
||||
|
||||
MIT.
|
||||
|
||||
### Screenshots
|
||||
|
||||
Home Page:
|
||||
|
||||

|
||||
|
||||
Home Page for mobile:
|
||||
|
||||

|
||||
|
||||
Home Page Hover Status for mobile:
|
||||
|
||||

|
||||
|
||||
Blog Show Page:
|
||||
|
||||

|
||||
|
||||
Blog Show Page Hover Status:
|
||||
|
||||

|
||||
|
||||
Admin Login Page:
|
||||
|
||||

|
||||
|
||||
Admin Dashboard Page:
|
||||
|
||||

|
||||
|
||||
Admin New Blog Page:
|
||||
|
||||

|
||||
|
||||
Admin Blogs Manage Page:
|
||||
|
||||

|
||||
|
|
101
README.zh-CN.md
|
@ -1,64 +1,65 @@
|
|||
WBlog
|
||||
=======
|
||||
[](https://travis-ci.org/windy/wblog)
|
||||
[](https://codeclimate.com/github/windy/wblog/maintainability)
|
||||
[](https://codeclimate.com/github/windy/wblog/test_coverage)
|
||||
[](https://codeclimate.com/github/windy/wblog)
|
||||
[](https://codeclimate.com/github/windy/wblog)
|
||||
|
||||
为移动而生的 Ruby on Rails 开源博客. WBlog 基于 MIT 协议, 自由使用.
|
||||
|
||||
现已全面支持 Ruby on Rails 5.2 版本!!!
|
||||
|
||||
* 用户极为友好的阅读体验
|
||||
* 自带干净的评论系统
|
||||
* 简洁而不简单的发布博客流程
|
||||
|
||||
访问我的博客以体验: <http://yafeilee.com>
|
||||
访问我的博客以体验: <http://yafeilee.me>
|
||||
|
||||
后台禁止爬虫, 使用: <http://yafeilee.com/admin> 访问, 用户名密码可配置.
|
||||
后台禁止爬虫, 使用: <http://yafeilee.me/admin> 访问, 用户名密码可配置.
|
||||
|
||||
截图如下: <#screenshots>
|
||||

|
||||
|
||||
|
||||
### WBlog 的设计目标
|
||||
### 为什么重写 WBlog
|
||||
|
||||
老的 WBlog 是两年前构建的, 体验越来越差, 而个人不喜欢托管博客到其他的站点, 又没有合适的 Ruby on Rails 博客系统.
|
||||
|
||||
* 优先以手机用户体验为主
|
||||
* 独立干净的评论系统
|
||||
* 干净的评论系统
|
||||
* 良好的博客语法高亮支持
|
||||
* 可邮件订阅
|
||||
* Markdown 支持
|
||||
* 尽可能独立
|
||||
* markdown, 简洁而不简单的后台
|
||||
* 要独立站点
|
||||
|
||||
### 特色
|
||||
|
||||
* 优先支持移动端访问
|
||||
* 响应式设计, 支持所有屏幕终端, 并且支持微信扫码继续阅读和分享
|
||||
* 自适应于所有屏幕终端, 方便微信分享与评论
|
||||
* 优先考虑移动用户, 可方便使用二维码扫描与关注
|
||||
* 自带评论系统, 干净而方便
|
||||
* Markdown 支持, 博客语法高亮, 方便技术性博客
|
||||
* 开源可商用, 定制能力强
|
||||
* markdown 支持, 博客语法高亮, 方便技术性博客
|
||||
* 开源可商用, 个性化能力超强 ( 与非独立博客相比 )
|
||||
|
||||
### 期望
|
||||
|
||||
成为 `Ruby on Rails` 下最好用的独立博客建站系统
|
||||
|
||||
### 开发环境
|
||||
### 本地学习
|
||||
|
||||
WBlog 是一个标准的 Ruby on Rails 应用. 开发环境依赖于:
|
||||
WBlog 是一个基本的博客系统, 使用它之前, 你需要准备一台 VPS 独立主机, 安装好 Ruby on Rails 与 Mongodb. 我希望你是熟悉 Ruby on Rails 的, 这样方便定制 WBlog, 现在 WBlog 还太小.
|
||||
|
||||
* Ruby ( = 2.3.1 )
|
||||
* Postgresql ( >= 9.x )
|
||||
假定你有环境后, 克隆本代码. 然后与往常的 Rails 项目一样, 先安装 mongodb
|
||||
|
||||
```shell
|
||||
# Install mongodb ( see how to install it on your platform )
|
||||
# on Mac, you can install it like this:
|
||||
brew install mongodb
|
||||
```
|
||||
|
||||
配置 WBlog:
|
||||
|
||||
```shell
|
||||
gem install bundler
|
||||
bundle install
|
||||
cp config/application.yml.example config/application.yml
|
||||
cp config/database.yml.example config/database.yml
|
||||
cp config/mongoid.yml.example config/mongoid.yml
|
||||
```
|
||||
|
||||
更新对应配置: application.yml & database.yml.
|
||||
|
||||
对于配置有不明白的地方, 可以来这里咨询.
|
||||
根据你个人情况, 更新对应的 application.yml & mongoid.yml.
|
||||
|
||||
就这样, 可以尝试启动了:
|
||||
|
||||
|
@ -66,22 +67,20 @@ WBlog 是一个标准的 Ruby on Rails 应用. 开发环境依赖于:
|
|||
rails s
|
||||
```
|
||||
|
||||
登录 http://localhsot:3000/admin 来发布第一篇博客.
|
||||
|
||||
### 发布应用
|
||||
|
||||
WBlog 采用了 `mina` 作为自动化发布工具, 使用 `nginx`, `puma` 为相关容器.
|
||||
WBlog 采用了 `mina` 作为自动化发布工具, 使用 `nginx`, `unicorn` 为相关容器.
|
||||
|
||||
对应的发布流程在: [WBlog 的发布流程](https://github.com/windy/wblog/wiki)
|
||||
|
||||
### 技术栈
|
||||
|
||||
* Ruby on Rails 5.2.0
|
||||
* Ruby 2.3.1
|
||||
* Foundation 6
|
||||
* Ruby on Rails 4.1.8 / Ruby 2.0
|
||||
* AngularJS
|
||||
* Foundation 5
|
||||
* mina
|
||||
* slim
|
||||
* Postgresql
|
||||
* Mongodb
|
||||
|
||||
|
||||
## Ruby 相关开源博客推荐
|
||||
|
@ -91,41 +90,3 @@ WBlog 采用了 `mina` 作为自动化发布工具, 使用 `nginx`, `puma` 为
|
|||
* octopress( Github Pages ): <http://octopress.org/>
|
||||
* middleman( Ruby Gem, Static ): <https://github.com/middleman/middleman>
|
||||
* robbin_site( Padrino ): <https://github.com/robbin/robbin_site>
|
||||
|
||||
### Screenshots
|
||||
|
||||
首页:
|
||||
|
||||

|
||||
|
||||
小屏首页:
|
||||
|
||||

|
||||
|
||||
展开的小屏首页:
|
||||
|
||||

|
||||
|
||||
博客详情页:
|
||||
|
||||

|
||||
|
||||
展开的博客详情页:
|
||||
|
||||

|
||||
|
||||
管理员登录页:
|
||||
|
||||

|
||||
|
||||
管理页面板:
|
||||
|
||||

|
||||
|
||||
发布新博客页:
|
||||
|
||||

|
||||
|
||||
博客管理页:
|
||||
|
||||

|
||||
|
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 458 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB |
|
@ -1,13 +0,0 @@
|
|||
$(document).on 'turbolinks:load', ->
|
||||
if $('.about-page').length > 0
|
||||
$(window).scroll ()->
|
||||
if $(this).scrollTop() > 0
|
||||
$('.top-bar-wrapper').addClass('active')
|
||||
else
|
||||
$('.top-bar-wrapper').removeClass('active')
|
||||
|
||||
$('#about-top-bar').ddscrollSpy
|
||||
highlightclass: 'active'
|
||||
|
||||
$('.intro').ddscrollSpy
|
||||
highlightclass: 'active'
|
|
@ -3,42 +3,24 @@
|
|||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
#
|
||||
|
||||
$(document).on 'turbolinks:load', ->
|
||||
$(document).ready ->
|
||||
$('a#upload_photo').click ->
|
||||
$('input[type=file]').show().focus().click().hide()
|
||||
false
|
||||
|
||||
$('#tabs').on 'change.zf.tabs', ()->
|
||||
if $('#preview:visible').length > 0
|
||||
$('#preview').text('Loading...')
|
||||
$.ajax
|
||||
url: '/admin/posts/preview'
|
||||
type: 'POST'
|
||||
data:
|
||||
content: $('#content-input').val()
|
||||
success: (data)->
|
||||
$('#preview').html(data)
|
||||
|
||||
$('a.tag').click (event)->
|
||||
event.preventDefault()
|
||||
new_labels = $(this).text()
|
||||
if $('#labels').val() == ''
|
||||
labels = new_labels
|
||||
else
|
||||
labels = $('#labels').val() + ", #{new_labels}"
|
||||
$('#labels').val(labels)
|
||||
|
||||
|
||||
opt =
|
||||
type: 'POST'
|
||||
url: "/photos"
|
||||
success: (data,status,xhr)->
|
||||
txtBox = $("#content-input")
|
||||
txtBox = $("#post_content")
|
||||
caret_pos = txtBox.caret('pos')
|
||||
src_merged = "\n" + data + "\n"
|
||||
source = txtBox.val()
|
||||
before_text = source.slice(0, caret_pos)
|
||||
txtBox.val(before_text + src_merged + source.slice(caret_pos+1, source.count))
|
||||
txtBox.caret('pos',caret_pos + src_merged.length)
|
||||
txtBox.scope().content = txtBox.val()
|
||||
txtBox.focus()
|
||||
|
||||
|
||||
$('input[type=file]').fileUpload opt
|
|
@ -0,0 +1,589 @@
|
|||
/**
|
||||
* x is a value between 0 and 1, indicating where in the animation you are.
|
||||
*/
|
||||
var duScrollDefaultEasing = function (x) {
|
||||
'use strict';
|
||||
|
||||
if(x < 0.5) {
|
||||
return Math.pow(x*2, 2)/2;
|
||||
}
|
||||
return 1-Math.pow((1-x)*2, 2)/2;
|
||||
};
|
||||
|
||||
angular.module('duScroll', [
|
||||
'duScroll.scrollspy',
|
||||
'duScroll.smoothScroll',
|
||||
'duScroll.scrollContainer',
|
||||
'duScroll.spyContext',
|
||||
'duScroll.scrollHelpers'
|
||||
])
|
||||
//Default animation duration for smoothScroll directive
|
||||
.value('duScrollDuration', 350)
|
||||
//Scrollspy debounce interval, set to 0 to disable
|
||||
.value('duScrollSpyWait', 100)
|
||||
//Wether or not multiple scrollspies can be active at once
|
||||
.value('duScrollGreedy', false)
|
||||
//Default offset for smoothScroll directive
|
||||
.value('duScrollOffset', 0)
|
||||
//Default easing function for scroll animation
|
||||
.value('duScrollEasing', duScrollDefaultEasing);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation'])
|
||||
.run(["$window", "$q", "cancelAnimation", "requestAnimation", "duScrollEasing", "duScrollDuration", "duScrollOffset", function($window, $q, cancelAnimation, requestAnimation, duScrollEasing, duScrollDuration, duScrollOffset) {
|
||||
'use strict';
|
||||
|
||||
var proto = angular.element.prototype;
|
||||
|
||||
var isDocument = function(el) {
|
||||
return (typeof HTMLDocument !== 'undefined' && el instanceof HTMLDocument) || (el.nodeType && el.nodeType === el.DOCUMENT_NODE);
|
||||
};
|
||||
|
||||
var isElement = function(el) {
|
||||
return (typeof HTMLElement !== 'undefined' && el instanceof HTMLElement) || (el.nodeType && el.nodeType === el.ELEMENT_NODE);
|
||||
};
|
||||
|
||||
var unwrap = function(el) {
|
||||
return isElement(el) || isDocument(el) ? el : el[0];
|
||||
};
|
||||
|
||||
proto.scrollTo = function(left, top, duration, easing) {
|
||||
var aliasFn;
|
||||
if(angular.isElement(left)) {
|
||||
aliasFn = this.scrollToElement;
|
||||
} else if(duration) {
|
||||
aliasFn = this.scrollToAnimated;
|
||||
}
|
||||
if(aliasFn) {
|
||||
return aliasFn.apply(this, arguments);
|
||||
}
|
||||
var el = unwrap(this);
|
||||
if(isDocument(el)) {
|
||||
return $window.scrollTo(left, top);
|
||||
}
|
||||
el.scrollLeft = left;
|
||||
el.scrollTop = top;
|
||||
};
|
||||
|
||||
var scrollAnimation, deferred;
|
||||
proto.scrollToAnimated = function(left, top, duration, easing) {
|
||||
if(duration && !easing) {
|
||||
easing = duScrollEasing;
|
||||
}
|
||||
var startLeft = this.scrollLeft(),
|
||||
startTop = this.scrollTop(),
|
||||
deltaLeft = Math.round(left - startLeft),
|
||||
deltaTop = Math.round(top - startTop);
|
||||
|
||||
var startTime = null;
|
||||
var el = this;
|
||||
|
||||
var cancelOnEvents = 'scroll mousedown mousewheel touchmove keydown';
|
||||
var cancelScrollAnimation = function($event) {
|
||||
if (!$event || $event.which > 0) {
|
||||
el.unbind(cancelOnEvents, cancelScrollAnimation);
|
||||
cancelAnimation(scrollAnimation);
|
||||
deferred.reject();
|
||||
scrollAnimation = null;
|
||||
}
|
||||
};
|
||||
|
||||
if(scrollAnimation) {
|
||||
cancelScrollAnimation();
|
||||
}
|
||||
deferred = $q.defer();
|
||||
|
||||
if(!deltaLeft && !deltaTop) {
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var animationStep = function(timestamp) {
|
||||
if (startTime === null) {
|
||||
startTime = timestamp;
|
||||
}
|
||||
|
||||
var progress = timestamp - startTime;
|
||||
var percent = (progress >= duration ? 1 : easing(progress/duration));
|
||||
|
||||
el.scrollTo(
|
||||
startLeft + Math.ceil(deltaLeft * percent),
|
||||
startTop + Math.ceil(deltaTop * percent)
|
||||
);
|
||||
if(percent < 1) {
|
||||
scrollAnimation = requestAnimation(animationStep);
|
||||
} else {
|
||||
el.unbind(cancelOnEvents, cancelScrollAnimation);
|
||||
scrollAnimation = null;
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
//Fix random mobile safari bug when scrolling to top by hitting status bar
|
||||
el.scrollTo(startLeft, startTop);
|
||||
|
||||
el.bind(cancelOnEvents, cancelScrollAnimation);
|
||||
|
||||
scrollAnimation = requestAnimation(animationStep);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
proto.scrollToElement = function(target, offset, duration, easing) {
|
||||
var el = unwrap(this);
|
||||
if(!angular.isNumber(offset) || isNaN(offset)) {
|
||||
offset = duScrollOffset;
|
||||
}
|
||||
var top = this.scrollTop() + unwrap(target).getBoundingClientRect().top - offset;
|
||||
if(isElement(el)) {
|
||||
top -= el.getBoundingClientRect().top;
|
||||
}
|
||||
return this.scrollTo(0, top, duration, easing);
|
||||
};
|
||||
|
||||
var overloaders = {
|
||||
scrollLeft: function(value, duration, easing) {
|
||||
if(angular.isNumber(value)) {
|
||||
return this.scrollTo(value, this.scrollTop(), duration, easing);
|
||||
}
|
||||
var el = unwrap(this);
|
||||
if(isDocument(el)) {
|
||||
return $window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft;
|
||||
}
|
||||
return el.scrollLeft;
|
||||
},
|
||||
scrollTop: function(value, duration, easing) {
|
||||
if(angular.isNumber(value)) {
|
||||
return this.scrollTo(this.scrollTop(), value, duration, easing);
|
||||
}
|
||||
var el = unwrap(this);
|
||||
if(isDocument(el)) {
|
||||
return $window.scrollY || document.documentElement.scrollTop || document.body.scrollTop;
|
||||
}
|
||||
return el.scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
proto.scrollToElementAnimated = function(target, offset, duration, easing) {
|
||||
return this.scrollToElement(target, offset, duration || duScrollDuration, easing);
|
||||
};
|
||||
|
||||
proto.scrollTopAnimated = function(top, duration, easing) {
|
||||
return this.scrollTop(top, duration || duScrollDuration, easing);
|
||||
};
|
||||
|
||||
proto.scrollLeftAnimated = function(left, duration, easing) {
|
||||
return this.scrollLeft(left, duration || duScrollDuration, easing);
|
||||
};
|
||||
|
||||
//Add duration and easing functionality to existing jQuery getter/setters
|
||||
var overloadScrollPos = function(superFn, overloadFn) {
|
||||
return function(value, duration, easing) {
|
||||
if(duration) {
|
||||
return overloadFn.apply(this, arguments);
|
||||
}
|
||||
return superFn.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
for(var methodName in overloaders) {
|
||||
proto[methodName] = (proto[methodName] ? overloadScrollPos(proto[methodName], overloaders[methodName]) : overloaders[methodName]);
|
||||
}
|
||||
}]);
|
||||
|
||||
|
||||
//Adapted from https://gist.github.com/paulirish/1579671
|
||||
angular.module('duScroll.polyfill', [])
|
||||
.factory('polyfill', ["$window", function($window) {
|
||||
'use strict';
|
||||
|
||||
var vendors = ['webkit', 'moz', 'o', 'ms'];
|
||||
|
||||
return function(fnName, fallback) {
|
||||
if($window[fnName]) {
|
||||
return $window[fnName];
|
||||
}
|
||||
var suffix = fnName.substr(0, 1).toUpperCase() + fnName.substr(1);
|
||||
for(var key, i = 0; i < vendors.length; i++) {
|
||||
key = vendors[i]+suffix;
|
||||
if($window[key]) {
|
||||
return $window[key];
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
}]);
|
||||
|
||||
angular.module('duScroll.requestAnimation', ['duScroll.polyfill'])
|
||||
.factory('requestAnimation', ["polyfill", "$timeout", function(polyfill, $timeout) {
|
||||
'use strict';
|
||||
|
||||
var lastTime = 0;
|
||||
var fallback = function(callback, element) {
|
||||
var currTime = new Date().getTime();
|
||||
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||
var id = $timeout(function() { callback(currTime + timeToCall); },
|
||||
timeToCall);
|
||||
lastTime = currTime + timeToCall;
|
||||
return id;
|
||||
};
|
||||
|
||||
return polyfill('requestAnimationFrame', fallback);
|
||||
}])
|
||||
.factory('cancelAnimation', ["polyfill", "$timeout", function(polyfill, $timeout) {
|
||||
'use strict';
|
||||
|
||||
var fallback = function(promise) {
|
||||
$timeout.cancel(promise);
|
||||
};
|
||||
|
||||
return polyfill('cancelAnimationFrame', fallback);
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])
|
||||
.factory('spyAPI', ["$rootScope", "$timeout", "scrollContainerAPI", "duScrollGreedy", "duScrollSpyWait", function($rootScope, $timeout, scrollContainerAPI, duScrollGreedy, duScrollSpyWait) {
|
||||
'use strict';
|
||||
|
||||
var createScrollHandler = function(context) {
|
||||
var timer = false, queued = false;
|
||||
var handler = function() {
|
||||
queued = false;
|
||||
var container = context.container,
|
||||
containerEl = container[0],
|
||||
containerOffset = 0;
|
||||
|
||||
if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {
|
||||
containerOffset = containerEl.getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
var i, currentlyActive, toBeActive, spies, spy, pos;
|
||||
spies = context.spies;
|
||||
currentlyActive = context.currentlyActive;
|
||||
toBeActive = undefined;
|
||||
|
||||
for(i = 0; i < spies.length; i++) {
|
||||
spy = spies[i];
|
||||
pos = spy.getTargetPosition();
|
||||
if (!pos) continue;
|
||||
|
||||
if(pos.top + spy.offset - containerOffset < 20 && (pos.top*-1 + containerOffset) < pos.height) {
|
||||
if(!toBeActive || toBeActive.top < pos.top) {
|
||||
toBeActive = {
|
||||
top: pos.top,
|
||||
spy: spy
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if(toBeActive) {
|
||||
toBeActive = toBeActive.spy;
|
||||
}
|
||||
if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;
|
||||
if(currentlyActive) {
|
||||
currentlyActive.$element.removeClass('active');
|
||||
$rootScope.$broadcast('duScrollspy:becameInactive', currentlyActive.$element);
|
||||
}
|
||||
if(toBeActive) {
|
||||
toBeActive.$element.addClass('active');
|
||||
$rootScope.$broadcast('duScrollspy:becameActive', toBeActive.$element);
|
||||
}
|
||||
context.currentlyActive = toBeActive;
|
||||
};
|
||||
|
||||
if(!duScrollSpyWait) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
//Debounce for potential performance savings
|
||||
return function() {
|
||||
if(!timer) {
|
||||
handler();
|
||||
timer = $timeout(function() {
|
||||
timer = false;
|
||||
if(queued) {
|
||||
handler();
|
||||
}
|
||||
}, duScrollSpyWait, false);
|
||||
} else {
|
||||
queued = true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var contexts = {};
|
||||
|
||||
var createContext = function($scope) {
|
||||
var id = $scope.$id;
|
||||
var context = {
|
||||
spies: []
|
||||
};
|
||||
|
||||
context.handler = createScrollHandler(context);
|
||||
contexts[id] = context;
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
destroyContext($scope);
|
||||
});
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
var destroyContext = function($scope) {
|
||||
var id = $scope.$id;
|
||||
var context = contexts[id], container = context.container;
|
||||
if(container) {
|
||||
container.off('scroll', context.handler);
|
||||
}
|
||||
delete contexts[id];
|
||||
};
|
||||
|
||||
var defaultContextId = createContext($rootScope);
|
||||
|
||||
var getContextForScope = function(scope) {
|
||||
if(contexts[scope.$id]) {
|
||||
return contexts[scope.$id];
|
||||
}
|
||||
if(scope.$parent) {
|
||||
return getContextForScope(scope.$parent);
|
||||
}
|
||||
return contexts[defaultContextId];
|
||||
};
|
||||
|
||||
var getContextForSpy = function(spy) {
|
||||
var context, contextId, scope = spy.$element.scope();
|
||||
if(scope) {
|
||||
return getContextForScope(scope);
|
||||
}
|
||||
//No scope, most likely destroyed
|
||||
for(contextId in contexts) {
|
||||
context = contexts[contextId];
|
||||
if(context.spies.indexOf(spy) !== -1) {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var isElementInDocument = function(element) {
|
||||
while (element.parentNode) {
|
||||
element = element.parentNode;
|
||||
if (element === document) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var addSpy = function(spy) {
|
||||
var context = getContextForSpy(spy);
|
||||
if (!context) return;
|
||||
context.spies.push(spy);
|
||||
if (!context.container || !isElementInDocument(context.container)) {
|
||||
if(context.container) {
|
||||
context.container.off('scroll', context.handler);
|
||||
}
|
||||
context.container = scrollContainerAPI.getContainer(spy.$element.scope());
|
||||
context.container.on('scroll', context.handler).triggerHandler('scroll');
|
||||
}
|
||||
};
|
||||
|
||||
var removeSpy = function(spy) {
|
||||
var context = getContextForSpy(spy);
|
||||
if(spy === context.currentlyActive) {
|
||||
context.currentlyActive = null;
|
||||
}
|
||||
var i = context.spies.indexOf(spy);
|
||||
if(i !== -1) {
|
||||
context.spies.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
addSpy: addSpy,
|
||||
removeSpy: removeSpy,
|
||||
createContext: createContext,
|
||||
destroyContext: destroyContext,
|
||||
getContextForScope: getContextForScope
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollContainerAPI', [])
|
||||
.factory('scrollContainerAPI', ["$document", function($document) {
|
||||
'use strict';
|
||||
|
||||
var containers = {};
|
||||
|
||||
var setContainer = function(scope, element) {
|
||||
var id = scope.$id;
|
||||
containers[id] = element;
|
||||
return id;
|
||||
};
|
||||
|
||||
var getContainerId = function(scope) {
|
||||
if(containers[scope.$id]) {
|
||||
return scope.$id;
|
||||
}
|
||||
if(scope.$parent) {
|
||||
return getContainerId(scope.$parent);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
var getContainer = function(scope) {
|
||||
var id = getContainerId(scope);
|
||||
return id ? containers[id] : $document;
|
||||
};
|
||||
|
||||
var removeContainer = function(scope) {
|
||||
var id = getContainerId(scope);
|
||||
if(id) {
|
||||
delete containers[id];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getContainerId: getContainerId,
|
||||
getContainer: getContainer,
|
||||
setContainer: setContainer,
|
||||
removeContainer: removeContainer
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.smoothScroll', ['duScroll.scrollHelpers', 'duScroll.scrollContainerAPI'])
|
||||
.directive('duSmoothScroll', ["duScrollDuration", "duScrollOffset", "scrollContainerAPI", function(duScrollDuration, duScrollOffset, scrollContainerAPI) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
link : function($scope, $element, $attr) {
|
||||
$element.on('click', function(e) {
|
||||
if(!$attr.href || $attr.href.indexOf('#') === -1) return;
|
||||
|
||||
var target = document.getElementById($attr.href.replace(/.*(?=#[^\s]+$)/, '').substring(1));
|
||||
if(!target || !target.getBoundingClientRect) return;
|
||||
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
|
||||
var offset = $attr.offset ? parseInt($attr.offset, 10) : duScrollOffset;
|
||||
var duration = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration;
|
||||
var container = scrollContainerAPI.getContainer($scope);
|
||||
|
||||
container.scrollToElement(
|
||||
angular.element(target),
|
||||
isNaN(offset) ? 0 : offset,
|
||||
isNaN(duration) ? 0 : duration
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.spyContext', ['duScroll.spyAPI'])
|
||||
.directive('duSpyContext', ["spyAPI", function(spyAPI) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: true,
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink($scope, iElement, iAttrs, controller) {
|
||||
spyAPI.createContext($scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollContainer', ['duScroll.scrollContainerAPI'])
|
||||
.directive('duScrollContainer', ["scrollContainerAPI", function(scrollContainerAPI){
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: true,
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink($scope, iElement, iAttrs, controller) {
|
||||
iAttrs.$observe('duScrollContainer', function(element) {
|
||||
if(angular.isString(element)) {
|
||||
element = document.getElementById(element);
|
||||
}
|
||||
|
||||
element = (angular.isElement(element) ? angular.element(element) : iElement);
|
||||
scrollContainerAPI.setContainer($scope, element);
|
||||
$scope.$on('$destroy', function() {
|
||||
scrollContainerAPI.removeContainer($scope);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollspy', ['duScroll.spyAPI'])
|
||||
.directive('duScrollspy', ["spyAPI", "duScrollOffset", "$timeout", "$rootScope", function(spyAPI, duScrollOffset, $timeout, $rootScope) {
|
||||
'use strict';
|
||||
|
||||
var Spy = function(targetElementOrId, $element, offset) {
|
||||
if(angular.isElement(targetElementOrId)) {
|
||||
this.target = targetElementOrId;
|
||||
} else if(angular.isString(targetElementOrId)) {
|
||||
this.targetId = targetElementOrId;
|
||||
}
|
||||
this.$element = $element;
|
||||
this.offset = offset;
|
||||
};
|
||||
|
||||
Spy.prototype.getTargetElement = function() {
|
||||
if (!this.target && this.targetId) {
|
||||
this.target = document.getElementById(this.targetId);
|
||||
}
|
||||
return this.target;
|
||||
};
|
||||
|
||||
Spy.prototype.getTargetPosition = function() {
|
||||
var target = this.getTargetElement();
|
||||
if(target) {
|
||||
return target.getBoundingClientRect();
|
||||
}
|
||||
};
|
||||
|
||||
Spy.prototype.flushTargetCache = function() {
|
||||
if(this.targetId) {
|
||||
this.target = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
link: function ($scope, $element, $attr) {
|
||||
var href = $attr.ngHref || $attr.href;
|
||||
var targetId;
|
||||
|
||||
if (href && href.indexOf('#') !== -1) {
|
||||
targetId = href.replace(/.*(?=#[^\s]+$)/, '').substring(1);
|
||||
} else if($attr.duScrollspy) {
|
||||
targetId = $attr.duScrollspy;
|
||||
}
|
||||
if(!targetId) return;
|
||||
|
||||
// Run this in the next execution loop so that the scroll context has a chance
|
||||
// to initialize
|
||||
$timeout(function() {
|
||||
var spy = new Spy(targetId, $element, -($attr.offset ? parseInt($attr.offset, 10) : duScrollOffset));
|
||||
spyAPI.addSpy(spy);
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
spyAPI.removeSpy(spy);
|
||||
});
|
||||
$scope.$on('$locationChangeSuccess', spy.flushTargetCache.bind(spy));
|
||||
$rootScope.$on('$stateChangeSuccess', spy.flushTargetCache.bind(spy));
|
||||
}, 0, false);
|
||||
}
|
||||
};
|
||||
}]);
|
|
@ -0,0 +1,15 @@
|
|||
#= require angular
|
||||
#= require angular-cookies
|
||||
#= require angular-resource
|
||||
#= require angular-sanitize
|
||||
#= require angular-scroll
|
||||
#= require_self
|
||||
#= require_tree ./angularjs
|
||||
|
||||
@app = angular.module('app', ['ngCookies', 'ngSanitize', 'duScroll'])
|
||||
@app.value('duScrollOffset', 30)
|
||||
|
||||
@app.config(["$httpProvider", (provider) ->
|
||||
provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
|
||||
provider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
||||
])
|
|
@ -0,0 +1,36 @@
|
|||
@app.controller 'AboutController', [ '$scope', '$timeout', '$http', ($scope, $timeout, $http)->
|
||||
$scope.type = null
|
||||
|
||||
$scope.click = (v)->
|
||||
if $scope.type == v
|
||||
$scope.type = null
|
||||
return
|
||||
$scope.type = v
|
||||
$scope.weixin_click = ->
|
||||
$scope.weixin = !$scope.weixin
|
||||
|
||||
#订阅功能
|
||||
|
||||
$scope.email = ''
|
||||
$scope.subscribe_success = null
|
||||
|
||||
$scope.email_validate = ->
|
||||
$scope.email.match(/@/)
|
||||
|
||||
$scope.subscribe = ()->
|
||||
$http
|
||||
url: '/subscribes'
|
||||
method: 'POST'
|
||||
params:
|
||||
email: $scope.email
|
||||
.success (res)->
|
||||
if res.success
|
||||
$scope.email = ''
|
||||
$scope.subscribe_success = true
|
||||
else
|
||||
$scope.subscribe_success = false
|
||||
$scope.subscribe_fail_msg = res.message
|
||||
$timeout ->
|
||||
$scope.subscribe_success = null
|
||||
, 3000
|
||||
]
|
|
@ -0,0 +1,14 @@
|
|||
@app.controller 'AboutScrollController', [ '$scope', '$document', ($scope, $document)->
|
||||
|
||||
about_id = angular.element(document.getElementById('about'))
|
||||
|
||||
$document.on 'scroll', ()=>
|
||||
$scope.$apply()
|
||||
|
||||
$scope.is_top = ()->
|
||||
$document.scrollTop() <= 0
|
||||
|
||||
|
||||
$scope.to_about = ()->
|
||||
$document.scrollToElementAnimated(about_id)
|
||||
]
|
|
@ -0,0 +1,35 @@
|
|||
@app.controller 'AdminPostsController', [ '$scope', '$http', '$location', '$timeout', '$cookies', '$sce', ($scope, $http, $location, $timeout, $cookies, $sce)->
|
||||
|
||||
$scope.body_active = true
|
||||
|
||||
$scope.init = (id)->
|
||||
url = '/admin/posts/' + id + '.json'
|
||||
$http
|
||||
url: url
|
||||
method: 'GET'
|
||||
.success (res)->
|
||||
$scope.title = res.title
|
||||
$scope.type = res.type
|
||||
$scope.labels = res.labels
|
||||
$scope.content = res.content
|
||||
|
||||
$scope.changeToBody = ->
|
||||
$scope.body_active = true
|
||||
|
||||
$scope.changeToPreview = ->
|
||||
$scope.body_active = false
|
||||
$scope.previewHTML = 'Loading...'
|
||||
$http.post '/admin/posts/preview', { content: $scope.content }
|
||||
.success (res)->
|
||||
$scope.previewHTML = res
|
||||
|
||||
$scope.trustAsPreviewHTML = ()->
|
||||
$sce.trustAsHtml($scope.previewHTML)
|
||||
|
||||
$scope.addTag = (e)->
|
||||
new_labels= $(e.target).text()
|
||||
if $scope.labels
|
||||
$scope.labels += ", #{new_labels}"
|
||||
else
|
||||
$scope.labels = new_labels
|
||||
]
|
|
@ -0,0 +1,21 @@
|
|||
@app.controller 'AdminSessionsController', [ '$scope', '$http', '$timeout', '$cookies', ($scope, $http, $timeout, $cookies)->
|
||||
url = '/admin/sessions'
|
||||
|
||||
$scope.login = ->
|
||||
$http
|
||||
url: url
|
||||
method: 'POST'
|
||||
data:
|
||||
username: $scope.username
|
||||
password: $scope.password
|
||||
.success (res)->
|
||||
if res.success
|
||||
urlback = $cookies.urlback || '/admin'
|
||||
window.location = urlback
|
||||
else
|
||||
$scope.password = ''
|
||||
$scope.error_msg = res.message
|
||||
$timeout ->
|
||||
$scope.error_msg = null
|
||||
, 5000
|
||||
]
|
|
@ -0,0 +1,43 @@
|
|||
@app.controller 'ArchivesController',[ '$scope', '$http', '$location', '$timeout', '$cookies', ($scope, $http, $location, $timeout, $cookies)->
|
||||
url = window.location.pathname + ".json"
|
||||
start_with = $cookies.start_with if window.location.pathname == $cookies.start_with_type
|
||||
$http
|
||||
url: url
|
||||
method: 'GET'
|
||||
params:
|
||||
start_with: start_with
|
||||
all: true
|
||||
.success (res)->
|
||||
$scope.update_start_with(res.start_with)
|
||||
$scope.posts = res.posts
|
||||
|
||||
$scope.no_more_flag = false
|
||||
$scope.loading_flag = false
|
||||
|
||||
$scope.load = ()->
|
||||
$scope.loading_flag = true
|
||||
$http(
|
||||
url: url
|
||||
method: 'GET'
|
||||
params:
|
||||
start_with: $scope.start_with
|
||||
type: $scope.type
|
||||
).success (res)->
|
||||
$scope.no_more_flag = true if res.posts.length == 0
|
||||
$scope.update_start_with(res.start_with)
|
||||
$scope.posts = $scope.posts.concat(res.posts)
|
||||
$timeout ->
|
||||
$scope.loading_flag = false
|
||||
, 500
|
||||
$timeout ->
|
||||
$scope.no_more_flag = false
|
||||
, 3000
|
||||
|
||||
$scope.visit = (id)->
|
||||
"/blogs/" + id
|
||||
|
||||
$scope.update_start_with = (start_with)->
|
||||
$scope.start_with = start_with
|
||||
$cookies.start_with_type = window.location.pathname
|
||||
$cookies.start_with = start_with
|
||||
]
|
|
@ -0,0 +1,35 @@
|
|||
@app.controller 'CommentsController', ['$scope', '$http', '$location', '$timeout', '$cookies', ($scope, $http, $location, $timeout, $cookies)->
|
||||
url = window.location.pathname + "/comments.json"
|
||||
|
||||
$scope.name = $cookies.name
|
||||
$scope.email = $cookies.email
|
||||
|
||||
$http.get(url).success (data)->
|
||||
$scope.comments = data
|
||||
|
||||
$scope.publish_success = null
|
||||
|
||||
$scope.submit = ->
|
||||
$scope.submitting = true
|
||||
$cookies.name = $scope.name
|
||||
$cookies.email = $scope.email
|
||||
comment = { content: $scope.content, name: $scope.name, email: $scope.email }
|
||||
$http.post(url, comment)
|
||||
.success (res)->
|
||||
if res.success
|
||||
$scope.publish_success = true
|
||||
$scope.content = ''
|
||||
$scope.comments.unshift(res.data)
|
||||
else
|
||||
$scope.publish_success = false
|
||||
$scope.publish_fail_msg = res.message
|
||||
.error (data, status)->
|
||||
$scope.publish_success = false
|
||||
$scope.publish_fail_msg = 'Network Error, Retry for a moment, Status Code: ' + status
|
||||
.finally ->
|
||||
$scope.submitting = false
|
||||
$scope.timeout = $timeout ->
|
||||
$timeout.cancel($scope.timeout)
|
||||
$scope.publish_success = null
|
||||
, 5*1000
|
||||
]
|
|
@ -0,0 +1,9 @@
|
|||
@app.directive 'ngInitial', ->
|
||||
restrict: 'A',
|
||||
controller: [
|
||||
'$scope', '$element', '$attrs', '$parse', ($scope, $element, $attrs, $parse)->
|
||||
val = $attrs.ngInitial || $attrs.value || $attrs.$$element.text()
|
||||
getter = $parse($attrs.ngModel)
|
||||
setter = getter.assign
|
||||
setter($scope, val)
|
||||
]
|
|
@ -0,0 +1,38 @@
|
|||
@app.controller 'LikesController', ['$scope', '$http', '$location', '$cookies', ($scope, $http, $location, $cookies)->
|
||||
url = window.location.pathname + "/likes"
|
||||
|
||||
$http.get url
|
||||
.success (res)->
|
||||
$scope.count = res.count
|
||||
|
||||
$scope.like = $cookies.like
|
||||
|
||||
if $scope.like
|
||||
$http
|
||||
url: window.location.pathname + "/likes/#{$scope.like}/is_liked"
|
||||
method: 'GET'
|
||||
.success (res)->
|
||||
if res == true
|
||||
$scope.is_liked = true
|
||||
else
|
||||
$scope.is_liked = false
|
||||
else
|
||||
$scope.is_liked = false
|
||||
|
||||
$scope.submit = ->
|
||||
$http.post url
|
||||
.success (res)->
|
||||
if res.success
|
||||
$scope.like = $cookies.like = res.id
|
||||
$scope.count = res.count
|
||||
$scope.is_liked = true
|
||||
|
||||
$scope.cancel = ->
|
||||
$http.delete url + "/" + $scope.like
|
||||
.success (res)->
|
||||
$scope.count = res.count
|
||||
$scope.is_liked = false
|
||||
# anyway, clear cookie
|
||||
delete $cookies["like"]
|
||||
$scope.like = null
|
||||
]
|
|
@ -0,0 +1,4 @@
|
|||
@app.controller 'QRCodesController', [ '$scope', ($scope)->
|
||||
$scope.show = ->
|
||||
$scope.qrcode = ! $scope.qrcode
|
||||
]
|
|
@ -0,0 +1,10 @@
|
|||
@app.controller 'SubscribesController', [ '$scope', '$http', ($scope, $http)->
|
||||
$scope.cancel = ()->
|
||||
$http
|
||||
url: '/subscribes/cancel'
|
||||
method: 'POST'
|
||||
params:
|
||||
email: $scope.email
|
||||
.success (res)->
|
||||
window.location = '/'
|
||||
]
|
|
@ -1,16 +1,13 @@
|
|||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require turbolinks
|
||||
//= require action_cable
|
||||
//= require foundation
|
||||
//= require js.cookie
|
||||
//= require foundation/foundation
|
||||
//= require foundation/foundation.alert
|
||||
//= require foundation/foundation.topbar
|
||||
//= require foundation/foundation.offcanvas
|
||||
//= require foundation/foundation.magellan
|
||||
//= require angularjs
|
||||
//= require 'jquery.html5-fileupload'
|
||||
//= require jquery.atwho
|
||||
//= require ddscrollspy
|
||||
//= require qrcode
|
||||
//= require cable
|
||||
//= require_tree .
|
||||
|
||||
$(document).on('turbolinks:load', function(){
|
||||
$(document).foundation();
|
||||
});
|
||||
|
||||
$(function(){ $(document).foundation(); });
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
@App ||= {}
|
|
@ -1,12 +0,0 @@
|
|||
$(document).on 'turbolinks:load', ->
|
||||
$('#alert-container .close-button').click ()->
|
||||
$('#alert-container').hide()
|
||||
|
||||
if $('#blog-show-page').length > 0
|
||||
window.App.cable ||= ActionCable.createConsumer()
|
||||
if window.App.comment_channel
|
||||
window.App.comment_channel.unsubscribe()
|
||||
window.App.comment_channel = window.App.cable.subscriptions.create { channel: "CommentChannel", post_id: $('#blog-show-page').data('post_id') },
|
||||
received: (data)->
|
||||
if data['not'] != Cookies.get('cable_id')
|
||||
$.get( $('#blog-show-page').data('url') )
|
|
@ -1,42 +0,0 @@
|
|||
class @GoogleAnalytics
|
||||
|
||||
@load: ->
|
||||
# Google Analytics depends on a global _gaq array. window is the global scope.
|
||||
window._gaq = []
|
||||
window._gaq.push ["_setAccount", GoogleAnalytics.analyticsId()]
|
||||
|
||||
# Create a script element and insert it in the DOM
|
||||
ga = document.createElement("script")
|
||||
ga.type = "text/javascript"
|
||||
ga.async = true
|
||||
ga.src = ((if "https:" is document.location.protocol then "https://ssl" else "http://www")) + ".google-analytics.com/ga.js"
|
||||
firstScript = document.getElementsByTagName("script")[0]
|
||||
firstScript.parentNode.insertBefore ga, firstScript
|
||||
|
||||
if typeof Turbolinks isnt 'undefined' and Turbolinks.supported
|
||||
document.addEventListener "turbolinks:load", (->
|
||||
GoogleAnalytics.trackPageview()
|
||||
), true
|
||||
else
|
||||
GoogleAnalytics.trackPageview()
|
||||
|
||||
@trackPageview: (url) ->
|
||||
unless GoogleAnalytics.isLocalRequest()
|
||||
if url
|
||||
window._gaq.push ["_trackPageview", url]
|
||||
else
|
||||
window._gaq.push ["_trackPageview"]
|
||||
window._gaq.push ["_trackPageLoadTime"]
|
||||
|
||||
@isLocalRequest: ->
|
||||
GoogleAnalytics.documentDomainIncludes "local"
|
||||
|
||||
@documentDomainIncludes: (str) ->
|
||||
document.domain.indexOf(str) isnt -1
|
||||
|
||||
@analyticsId: ->
|
||||
'<%= ENV['GOOGLE'] %>'
|
||||
|
||||
<% if ENV['GOOGLE'] %>
|
||||
GoogleAnalytics.load()
|
||||
<% end %>
|
11
vendor/assets/javascripts/jquery.atwho.js → app/assets/javascripts/jquery.atwho.js
Normal file → Executable file
|
@ -8,6 +8,13 @@
|
|||
*/
|
||||
|
||||
|
||||
/*
|
||||
本插件操作 textarea 或者 input 内的插入符
|
||||
只实现了获得插入符在文本框中的位置,我设置
|
||||
插入符的位置.
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
|
@ -78,7 +85,7 @@
|
|||
/ \
|
||||
< I really [[HATE] IE []]>
|
||||
\_endRange end-point.
|
||||
|
||||
|
||||
" > -1" mean the start end-point will be the same or right to the end end-point
|
||||
* simplelly, all in the end.
|
||||
*/
|
||||
|
@ -92,7 +99,7 @@
|
|||
I really[ [HATE] IE ]>
|
||||
<-[
|
||||
I reall[y [HATE] IE ]>
|
||||
|
||||
|
||||
will return how many unit have moved.
|
||||
*/
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
$(document).on 'turbolinks:load', ->
|
||||
|
||||
$('.like-button').click ->
|
||||
if $(this).hasClass('liked')
|
||||
$.ajax
|
||||
url: $(this).data('url') + '/' + Cookies.get('like')
|
||||
type: 'DELETE'
|
||||
success: (res)=>
|
||||
$(this).removeClass('liked')
|
||||
$(this).children('.count').text(res.count)
|
||||
Cookies.remove('like')
|
||||
else
|
||||
$.ajax
|
||||
url: $(this).data('url')
|
||||
type: 'POST'
|
||||
success: (res)=>
|
||||
$(this).addClass('liked')
|
||||
$(this).children('.count').text(res.count)
|
||||
Cookies.set('like', res.id)
|
|
@ -1,13 +0,0 @@
|
|||
$(document).on 'turbolinks:load', ->
|
||||
|
||||
if $('#qrcode-home').length > 0
|
||||
$('#qrcode-home').empty()
|
||||
new QRCode( $('#qrcode-home')[0], $('#qrcode-home').data('url') )
|
||||
|
||||
if $('#image-tag').length > 0
|
||||
$('#image-tag').empty()
|
||||
new QRCode( $('#image-tag')[0], $('#image-tag').data('url') )
|
||||
|
||||
$('#qrcode-link').click (event)->
|
||||
event.preventDefault()
|
||||
$('.social-share').toggle()
|
|
@ -1,567 +0,0 @@
|
|||
// Foundation for Sites Settings
|
||||
// -----------------------------
|
||||
//
|
||||
// Table of Contents:
|
||||
//
|
||||
// 1. Global
|
||||
// 2. Breakpoints
|
||||
// 3. The Grid
|
||||
// 4. Base Typography
|
||||
// 5. Typography Helpers
|
||||
// 6. Abide
|
||||
// 7. Accordion
|
||||
// 8. Accordion Menu
|
||||
// 9. Badge
|
||||
// 10. Breadcrumbs
|
||||
// 11. Button
|
||||
// 12. Button Group
|
||||
// 13. Callout
|
||||
// 14. Close Button
|
||||
// 15. Drilldown
|
||||
// 16. Dropdown
|
||||
// 17. Dropdown Menu
|
||||
// 18. Flex Video
|
||||
// 19. Forms
|
||||
// 20. Label
|
||||
// 21. Media Object
|
||||
// 22. Menu
|
||||
// 23. Meter
|
||||
// 24. Off-canvas
|
||||
// 25. Orbit
|
||||
// 26. Pagination
|
||||
// 27. Progress Bar
|
||||
// 28. Reveal
|
||||
// 29. Slider
|
||||
// 30. Switch
|
||||
// 31. Table
|
||||
// 32. Tabs
|
||||
// 33. Thumbnail
|
||||
// 34. Title Bar
|
||||
// 35. Tooltip
|
||||
// 36. Top Bar
|
||||
|
||||
@import 'util/util';
|
||||
|
||||
// 1. Global
|
||||
// ---------
|
||||
|
||||
$global-font-size: 100%;
|
||||
$global-width: rem-calc(1200);
|
||||
$global-lineheight: 1.5;
|
||||
$foundation-palette: (
|
||||
primary: #2199e8,
|
||||
secondary: #777,
|
||||
success: #3adb76,
|
||||
warning: #ffae00,
|
||||
alert: #ec5840,
|
||||
);
|
||||
$light-gray: #e6e6e6;
|
||||
$medium-gray: #cacaca;
|
||||
$dark-gray: #8a8a8a;
|
||||
$black: #333333;
|
||||
$white: #fefefe;
|
||||
$body-background: $white;
|
||||
$body-font-color: $black;
|
||||
$body-font-family: 'Helvetica Neue', 'Microsoft Yahei', Helvetica, Roboto, 'WenQuanYi Micro Hei', Arial, sans-serif;
|
||||
$body-antialiased: true;
|
||||
$global-margin: 1rem;
|
||||
$global-padding: 1rem;
|
||||
$global-weight-normal: normal;
|
||||
$global-weight-bold: bold;
|
||||
$global-radius: 0;
|
||||
$global-text-direction: ltr;
|
||||
$global-flexbox: false;
|
||||
$print-transparent-backgrounds: true;
|
||||
|
||||
@include add-foundation-colors;
|
||||
|
||||
// 2. Breakpoints
|
||||
// --------------
|
||||
|
||||
$breakpoints: (
|
||||
small: 0,
|
||||
medium: 640px,
|
||||
large: 1024px,
|
||||
xlarge: 1200px,
|
||||
xxlarge: 1440px,
|
||||
);
|
||||
$breakpoint-classes: (small medium large);
|
||||
|
||||
// 3. The Grid
|
||||
// -----------
|
||||
|
||||
$grid-row-width: $global-width;
|
||||
$grid-column-count: 12;
|
||||
$grid-column-gutter: (
|
||||
small: 20px,
|
||||
medium: 30px,
|
||||
);
|
||||
$grid-column-align-edge: true;
|
||||
$block-grid-max: 8;
|
||||
|
||||
// 4. Base Typography
|
||||
// ------------------
|
||||
|
||||
$header-font-family: $body-font-family;
|
||||
$header-font-weight: $global-weight-normal;
|
||||
$header-font-style: normal;
|
||||
$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace;
|
||||
$header-sizes: (
|
||||
small: (
|
||||
'h1': 24,
|
||||
'h2': 20,
|
||||
'h3': 19,
|
||||
'h4': 18,
|
||||
'h5': 17,
|
||||
'h6': 16,
|
||||
),
|
||||
medium: (
|
||||
'h1': 48,
|
||||
'h2': 40,
|
||||
'h3': 31,
|
||||
'h4': 25,
|
||||
'h5': 20,
|
||||
'h6': 16,
|
||||
),
|
||||
);
|
||||
$header-color: inherit;
|
||||
$header-lineheight: 1.4;
|
||||
$header-margin-bottom: 0.5rem;
|
||||
$header-text-rendering: optimizeLegibility;
|
||||
$small-font-size: 80%;
|
||||
$header-small-font-color: $medium-gray;
|
||||
$paragraph-lineheight: 1.6;
|
||||
$paragraph-margin-bottom: 1rem;
|
||||
$paragraph-text-rendering: optimizeLegibility;
|
||||
$code-color: $black;
|
||||
$code-font-family: $font-family-monospace;
|
||||
$code-font-weight: $global-weight-normal;
|
||||
$code-background: $light-gray;
|
||||
$code-border: 1px solid $medium-gray;
|
||||
$code-padding: rem-calc(2 5 1);
|
||||
$anchor-color: $primary-color;
|
||||
$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
|
||||
$anchor-text-decoration: none;
|
||||
$anchor-text-decoration-hover: none;
|
||||
$hr-width: $global-width;
|
||||
$hr-border: 1px solid $medium-gray;
|
||||
$hr-margin: rem-calc(20) auto;
|
||||
$list-lineheight: $paragraph-lineheight;
|
||||
$list-margin-bottom: $paragraph-margin-bottom;
|
||||
$list-style-type: disc;
|
||||
$list-style-position: outside;
|
||||
$list-side-margin: 1.25rem;
|
||||
$list-nested-side-margin: 1.25rem;
|
||||
$defnlist-margin-bottom: 1rem;
|
||||
$defnlist-term-weight: $global-weight-bold;
|
||||
$defnlist-term-margin-bottom: 0.3rem;
|
||||
$blockquote-color: $dark-gray;
|
||||
$blockquote-padding: rem-calc(9 20 0 19);
|
||||
$blockquote-border: 1px solid $medium-gray;
|
||||
$cite-font-size: rem-calc(13);
|
||||
$cite-color: $dark-gray;
|
||||
$keystroke-font: $font-family-monospace;
|
||||
$keystroke-color: $black;
|
||||
$keystroke-background: $light-gray;
|
||||
$keystroke-padding: rem-calc(2 4 0);
|
||||
$keystroke-radius: $global-radius;
|
||||
$abbr-underline: 1px dotted $black;
|
||||
|
||||
// 5. Typography Helpers
|
||||
// ---------------------
|
||||
|
||||
$lead-font-size: $global-font-size * 1.25;
|
||||
$lead-lineheight: 1.6;
|
||||
$subheader-lineheight: 1.4;
|
||||
$subheader-color: $dark-gray;
|
||||
$subheader-font-weight: $global-weight-normal;
|
||||
$subheader-margin-top: 0.2rem;
|
||||
$subheader-margin-bottom: 0.5rem;
|
||||
$stat-font-size: 2.5rem;
|
||||
|
||||
// 6. Abide
|
||||
// --------
|
||||
|
||||
$abide-inputs: true;
|
||||
$abide-labels: true;
|
||||
$input-background-invalid: map-get($foundation-palette, alert);
|
||||
$form-label-color-invalid: map-get($foundation-palette, alert);
|
||||
$input-error-color: map-get($foundation-palette, alert);
|
||||
$input-error-font-size: rem-calc(12);
|
||||
$input-error-font-weight: $global-weight-bold;
|
||||
|
||||
// 7. Accordion
|
||||
// ------------
|
||||
|
||||
$accordion-background: $white;
|
||||
$accordion-plusminus: true;
|
||||
$accordion-item-color: foreground($accordion-background, $primary-color);
|
||||
$accordion-item-background-hover: $light-gray;
|
||||
$accordion-item-padding: 1.25rem 1rem;
|
||||
$accordion-content-background: $white;
|
||||
$accordion-content-border: 1px solid $light-gray;
|
||||
$accordion-content-color: foreground($accordion-background, $primary-color);
|
||||
$accordion-content-padding: 1rem;
|
||||
|
||||
// 8. Accordion Menu
|
||||
// -----------------
|
||||
|
||||
$accordionmenu-arrows: true;
|
||||
$accordionmenu-arrow-color: $primary-color;
|
||||
|
||||
// 9. Badge
|
||||
// --------
|
||||
|
||||
$badge-background: $primary-color;
|
||||
$badge-color: foreground($badge-background);
|
||||
$badge-padding: 0.3em;
|
||||
$badge-minwidth: 2.1em;
|
||||
$badge-font-size: 0.6rem;
|
||||
|
||||
// 10. Breadcrumbs
|
||||
// ---------------
|
||||
|
||||
$breadcrumbs-margin: 0 0 $global-margin 0;
|
||||
$breadcrumbs-item-font-size: rem-calc(11);
|
||||
$breadcrumbs-item-color: $primary-color;
|
||||
$breadcrumbs-item-color-current: $black;
|
||||
$breadcrumbs-item-color-disabled: $medium-gray;
|
||||
$breadcrumbs-item-margin: 0.75rem;
|
||||
$breadcrumbs-item-uppercase: true;
|
||||
$breadcrumbs-item-slash: true;
|
||||
|
||||
// 11. Button
|
||||
// ----------
|
||||
|
||||
$button-padding: 1rem 2rem 1.0625rem 2rem;
|
||||
$button-margin: 0 0 $global-margin 0;
|
||||
$button-fill: solid;
|
||||
$button-background: $primary-color;
|
||||
$button-background-hover: scale-color($button-background, $lightness: -15%);
|
||||
$button-color: $white;
|
||||
$button-color-alt: $black;
|
||||
$button-radius: $global-radius;
|
||||
$button-sizes: (
|
||||
tiny: 0.6rem,
|
||||
small: 0.75rem,
|
||||
default: 0.9rem,
|
||||
large: 1.25rem,
|
||||
);
|
||||
$button-opacity-disabled: 0.25;
|
||||
|
||||
// 12. Button Group
|
||||
// ----------------
|
||||
|
||||
$buttongroup-margin: 1rem;
|
||||
$buttongroup-spacing: 1px;
|
||||
$buttongroup-child-selector: '.button';
|
||||
$buttongroup-expand-max: 6;
|
||||
|
||||
// 13. Callout
|
||||
// -----------
|
||||
|
||||
$callout-background: $white;
|
||||
$callout-background-fade: 85%;
|
||||
$callout-border: 1px solid rgba($black, 0.25);
|
||||
$callout-margin: 0 0 1rem 0;
|
||||
$callout-padding: 1rem;
|
||||
$callout-font-color: $body-font-color;
|
||||
$callout-font-color-alt: $body-background;
|
||||
$callout-radius: $global-radius;
|
||||
$callout-link-tint: 30%;
|
||||
|
||||
// 14. Close Button
|
||||
// ----------------
|
||||
|
||||
$closebutton-position: right top;
|
||||
$closebutton-offset-horizontal: 1rem;
|
||||
$closebutton-offset-vertical: 0.5rem;
|
||||
$closebutton-size: 2em;
|
||||
$closebutton-lineheight: 1;
|
||||
$closebutton-color: $dark-gray;
|
||||
$closebutton-color-hover: $black;
|
||||
|
||||
// 15. Drilldown
|
||||
// -------------
|
||||
|
||||
$drilldown-transition: transform 0.15s linear;
|
||||
$drilldown-arrows: true;
|
||||
$drilldown-arrow-color: $primary-color;
|
||||
$drilldown-background: $white;
|
||||
|
||||
// 16. Dropdown
|
||||
// ------------
|
||||
|
||||
$dropdown-padding: 1rem;
|
||||
$dropdown-border: 1px solid $medium-gray;
|
||||
$dropdown-font-size: 1rem;
|
||||
$dropdown-width: 300px;
|
||||
$dropdown-radius: $global-radius;
|
||||
$dropdown-sizes: (
|
||||
tiny: 100px,
|
||||
small: 200px,
|
||||
large: 400px,
|
||||
);
|
||||
|
||||
// 17. Dropdown Menu
|
||||
// -----------------
|
||||
|
||||
$dropdownmenu-arrows: true;
|
||||
$dropdownmenu-arrow-color: $anchor-color;
|
||||
$dropdownmenu-min-width: 200px;
|
||||
$dropdownmenu-background: $white;
|
||||
$dropdownmenu-border: 1px solid $medium-gray;
|
||||
|
||||
// 18. Flex Video
|
||||
// --------------
|
||||
|
||||
$flexvideo-margin-bottom: rem-calc(16);
|
||||
$flexvideo-ratio: 4 by 3;
|
||||
$flexvideo-ratio-widescreen: 16 by 9;
|
||||
|
||||
// 19. Forms
|
||||
// ---------
|
||||
|
||||
$fieldset-border: 1px solid $medium-gray;
|
||||
$fieldset-padding: rem-calc(20);
|
||||
$fieldset-margin: rem-calc(18 0);
|
||||
$legend-padding: rem-calc(0 3);
|
||||
$form-spacing: rem-calc(16);
|
||||
$helptext-color: $black;
|
||||
$helptext-font-size: rem-calc(13);
|
||||
$helptext-font-style: italic;
|
||||
$input-prefix-color: $black;
|
||||
$input-prefix-background: $light-gray;
|
||||
$input-prefix-border: 1px solid $medium-gray;
|
||||
$input-prefix-padding: 1rem;
|
||||
$form-label-color: $black;
|
||||
$form-label-font-size: rem-calc(14);
|
||||
$form-label-font-weight: $global-weight-normal;
|
||||
$form-label-line-height: 1.8;
|
||||
$select-background: $white;
|
||||
$select-triangle-color: $dark-gray;
|
||||
$select-radius: $global-radius;
|
||||
$input-color: $black;
|
||||
$input-placeholder-color: $medium-gray;
|
||||
$input-font-family: inherit;
|
||||
$input-font-size: rem-calc(16);
|
||||
$input-background: $white;
|
||||
$input-background-focus: $white;
|
||||
$input-background-disabled: $light-gray;
|
||||
$input-border: 1px solid $medium-gray;
|
||||
$input-border-focus: 1px solid $dark-gray;
|
||||
$input-shadow: inset 0 1px 2px rgba($black, 0.1);
|
||||
$input-shadow-focus: 0 0 5px $medium-gray;
|
||||
$input-cursor-disabled: not-allowed;
|
||||
$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out;
|
||||
$input-number-spinners: true;
|
||||
$input-radius: $global-radius;
|
||||
|
||||
// 20. Label
|
||||
// ---------
|
||||
|
||||
$label-background: $primary-color;
|
||||
$label-color: foreground($label-background);
|
||||
$label-font-size: 0.8rem;
|
||||
$label-padding: 0.33333rem 0.5rem;
|
||||
$label-radius: $global-radius;
|
||||
|
||||
// 21. Media Object
|
||||
// ----------------
|
||||
|
||||
$mediaobject-margin-bottom: $global-margin;
|
||||
$mediaobject-section-padding: $global-padding;
|
||||
$mediaobject-image-width-stacked: 100%;
|
||||
|
||||
// 22. Menu
|
||||
// --------
|
||||
|
||||
$menu-margin: 0;
|
||||
$menu-margin-nested: 1rem;
|
||||
$menu-item-padding: 0.7rem 1rem;
|
||||
$menu-item-color-active: $white;
|
||||
$menu-item-background-active: map-get($foundation-palette, primary);
|
||||
$menu-icon-spacing: 0.25rem;
|
||||
|
||||
// 23. Meter
|
||||
// ---------
|
||||
|
||||
$meter-height: 1rem;
|
||||
$meter-radius: $global-radius;
|
||||
$meter-background: $medium-gray;
|
||||
$meter-fill-good: $success-color;
|
||||
$meter-fill-medium: $warning-color;
|
||||
$meter-fill-bad: $alert-color;
|
||||
|
||||
// 24. Off-canvas
|
||||
// --------------
|
||||
|
||||
$offcanvas-size: 250px;
|
||||
$offcanvas-background: #474747;
|
||||
$offcanvas-zindex: -1;
|
||||
$offcanvas-transition-length: 0.5s;
|
||||
$offcanvas-transition-timing: ease;
|
||||
$offcanvas-fixed-reveal: true;
|
||||
$offcanvas-exit-background: rgba($white, 0.25);
|
||||
$maincontent-class: 'off-canvas-content';
|
||||
$maincontent-shadow: 0 0 10px rgba($black, 0.5);
|
||||
|
||||
// 25. Orbit
|
||||
// ---------
|
||||
|
||||
$orbit-bullet-background: $medium-gray;
|
||||
$orbit-bullet-background-active: $dark-gray;
|
||||
$orbit-bullet-diameter: 1.2rem;
|
||||
$orbit-bullet-margin: 0.1rem;
|
||||
$orbit-bullet-margin-top: 0.8rem;
|
||||
$orbit-bullet-margin-bottom: 0.8rem;
|
||||
$orbit-caption-background: rgba($black, 0.5);
|
||||
$orbit-caption-padding: 1rem;
|
||||
$orbit-control-background-hover: rgba($black, 0.5);
|
||||
$orbit-control-padding: 1rem;
|
||||
$orbit-control-zindex: 10;
|
||||
|
||||
// 26. Pagination
|
||||
// --------------
|
||||
|
||||
$pagination-font-size: rem-calc(14);
|
||||
$pagination-margin-bottom: $global-margin;
|
||||
$pagination-item-color: $black;
|
||||
$pagination-item-padding: rem-calc(3 10);
|
||||
$pagination-item-spacing: rem-calc(1);
|
||||
$pagination-radius: $global-radius;
|
||||
$pagination-item-background-hover: $light-gray;
|
||||
$pagination-item-background-current: $primary-color;
|
||||
$pagination-item-color-current: foreground($pagination-item-background-current);
|
||||
$pagination-item-color-disabled: $medium-gray;
|
||||
$pagination-ellipsis-color: $black;
|
||||
$pagination-mobile-items: false;
|
||||
$pagination-arrows: true;
|
||||
|
||||
// 27. Progress Bar
|
||||
// ----------------
|
||||
|
||||
$progress-height: 1rem;
|
||||
$progress-background: $medium-gray;
|
||||
$progress-margin-bottom: $global-margin;
|
||||
$progress-meter-background: $primary-color;
|
||||
$progress-radius: $global-radius;
|
||||
|
||||
// 28. Reveal
|
||||
// ----------
|
||||
|
||||
$reveal-background: $white;
|
||||
$reveal-width: 600px;
|
||||
$reveal-max-width: $global-width;
|
||||
$reveal-padding: $global-padding;
|
||||
$reveal-border: 1px solid $medium-gray;
|
||||
$reveal-radius: $global-radius;
|
||||
$reveal-zindex: 1005;
|
||||
$reveal-overlay-background: rgba($black, 0.45);
|
||||
|
||||
// 29. Slider
|
||||
// ----------
|
||||
|
||||
$slider-width-vertical: 0.5rem;
|
||||
$slider-transition: all 0.2s ease-in-out;
|
||||
$slider-height: 0.5rem;
|
||||
$slider-background: $light-gray;
|
||||
$slider-fill-background: $medium-gray;
|
||||
$slider-handle-height: 1.4rem;
|
||||
$slider-handle-width: 1.4rem;
|
||||
$slider-handle-background: $primary-color;
|
||||
$slider-opacity-disabled: 0.25;
|
||||
$slider-radius: $global-radius;
|
||||
|
||||
// 30. Switch
|
||||
// ----------
|
||||
|
||||
$switch-background: $medium-gray;
|
||||
$switch-background-active: $primary-color;
|
||||
$switch-height: 2rem;
|
||||
$switch-height-tiny: 1.5rem;
|
||||
$switch-height-small: 1.75rem;
|
||||
$switch-height-large: 2.5rem;
|
||||
$switch-radius: $global-radius;
|
||||
$switch-margin: $global-margin;
|
||||
$switch-paddle-background: $white;
|
||||
$switch-paddle-offset: 0.25rem;
|
||||
$switch-paddle-radius: $global-radius;
|
||||
$switch-paddle-transition: all 0.25s ease-out;
|
||||
|
||||
// 31. Table
|
||||
// ---------
|
||||
|
||||
$table-background: $white;
|
||||
$table-color-scale: 5%;
|
||||
$table-border: 1px solid smart-scale($table-background, $table-color-scale);
|
||||
$table-padding: rem-calc(8 10 10);
|
||||
$table-hover-scale: 2%;
|
||||
$table-row-hover: darken($table-background, $table-hover-scale);
|
||||
$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale);
|
||||
$table-striped-background: smart-scale($table-background, $table-color-scale);
|
||||
$table-stripe: even;
|
||||
$table-head-background: smart-scale($table-background, $table-color-scale / 2);
|
||||
$table-foot-background: smart-scale($table-background, $table-color-scale);
|
||||
$table-head-font-color: $body-font-color;
|
||||
$show-header-for-stacked: false;
|
||||
|
||||
// 32. Tabs
|
||||
// --------
|
||||
|
||||
$tab-margin: 0;
|
||||
$tab-background: $white;
|
||||
$tab-background-active: $light-gray;
|
||||
$tab-item-font-size: rem-calc(16);
|
||||
$tab-item-background-hover: $white;
|
||||
$tab-item-padding: 1.25rem 1.5rem;
|
||||
$tab-expand-max: 6;
|
||||
$tab-content-background: $white;
|
||||
$tab-content-border: none;
|
||||
$tab-content-color: foreground($tab-background, $primary-color);
|
||||
$tab-content-padding: 1rem;
|
||||
|
||||
// 33. Thumbnail
|
||||
// -------------
|
||||
|
||||
$thumbnail-border: solid 4px $white;
|
||||
$thumbnail-margin-bottom: $global-margin;
|
||||
$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
|
||||
$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
|
||||
$thumbnail-transition: box-shadow 200ms ease-out;
|
||||
$thumbnail-radius: $global-radius;
|
||||
|
||||
// 34. Title Bar
|
||||
// -------------
|
||||
|
||||
$titlebar-background: $black;
|
||||
$titlebar-color: $white;
|
||||
$titlebar-padding: 0.5rem;
|
||||
$titlebar-text-font-weight: bold;
|
||||
$titlebar-icon-color: $white;
|
||||
$titlebar-icon-color-hover: $medium-gray;
|
||||
$titlebar-icon-spacing: 0.25rem;
|
||||
|
||||
// 35. Tooltip
|
||||
// -----------
|
||||
|
||||
$has-tip-font-weight: $global-weight-bold;
|
||||
$has-tip-border-bottom: dotted 1px $dark-gray;
|
||||
$tooltip-background-color: $black;
|
||||
$tooltip-color: $white;
|
||||
$tooltip-padding: 0.75rem;
|
||||
$tooltip-font-size: $small-font-size;
|
||||
$tooltip-pip-width: 0.75rem;
|
||||
$tooltip-pip-height: $tooltip-pip-width * 0.866;
|
||||
$tooltip-radius: $global-radius;
|
||||
|
||||
// 36. Top Bar
|
||||
// -----------
|
||||
|
||||
$topbar-padding: 0.5rem;
|
||||
$topbar-background: $black;
|
||||
$topbar-submenu-background: $topbar-background;
|
||||
$topbar-title-spacing: 1rem;
|
||||
$topbar-input-width: 200px;
|
||||
$topbar-unstack-breakpoint: medium;
|
||||
|
|
@ -1,13 +1,8 @@
|
|||
.self-introduce {
|
||||
@media screen and (min-width: 64em) {
|
||||
margin-top: 1.875rem;
|
||||
}
|
||||
margin-top: 2.2rem;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
}
|
||||
|
||||
p, .aboutme-index {
|
||||
color: #5D5D5D;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,69 +23,46 @@
|
|||
|
||||
|
||||
.about-page {
|
||||
.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.responsive-button {
|
||||
padding: 0.7rem 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
//@media only screen and (min-width: 40.063em) {
|
||||
@media screen and (max-width: 39.9375em) {
|
||||
@media only screen and (min-width: 40.063em) {
|
||||
.top-bar-wrapper {
|
||||
.top-bar, .top-bar-left, .top-bar-right {
|
||||
width: auto !important;
|
||||
background: 0 0;
|
||||
transition: background .5s ease-in-out,padding .5s ease-in-out;
|
||||
&.active {
|
||||
background: #000;
|
||||
border-bottom: 1px solid #666;
|
||||
|
||||
.top-bar {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.top-bar-wrapper {
|
||||
background: 0 0;
|
||||
transition: background .5s ease-in-out,padding .5s ease-in-out;
|
||||
&.active {
|
||||
background: #000;
|
||||
border-bottom: 1px solid #666;
|
||||
|
||||
.top-bar {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background: 0 0;
|
||||
margin: 1.5rem 0;
|
||||
.name {
|
||||
font-size: 1.325rem;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar ul{
|
||||
& {
|
||||
background: 0 0;
|
||||
}
|
||||
li, li a {
|
||||
background: 0 0;
|
||||
font-size: 1rem;
|
||||
color: #fefefe;
|
||||
margin: 1.5rem 0;
|
||||
.name h1 {
|
||||
font-size: 1.325rem;
|
||||
}
|
||||
}
|
||||
|
||||
li a:hover {
|
||||
background: #666;
|
||||
}
|
||||
.top-bar-section ul{
|
||||
li, li a {
|
||||
background: 0 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
li a.active {
|
||||
background: #4C4C4C;
|
||||
li a:hover {
|
||||
background: #666;
|
||||
}
|
||||
|
||||
li a.active {
|
||||
background: #4C4C4C;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
p {
|
||||
font-size: 1.275rem;
|
||||
|
@ -110,7 +82,7 @@
|
|||
text-align: center;
|
||||
|
||||
.heading, .sub-heading {
|
||||
color: #eee;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.version {
|
||||
|
@ -128,7 +100,7 @@
|
|||
}
|
||||
|
||||
.circle {
|
||||
color: #eee;
|
||||
color: #fff;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
font-size: 3rem;
|
||||
|
@ -144,24 +116,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
#about {
|
||||
.wrapper {
|
||||
.time {
|
||||
color: #aaa;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
p {
|
||||
font-size: 1rem;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
ul > li {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#about, #skill {
|
||||
background-color: #000;
|
||||
color: #eee;
|
||||
|
@ -208,7 +162,6 @@
|
|||
background-color: rgba(63, 63, 63, 0.6);
|
||||
list-style: none;
|
||||
padding: 2rem 1rem;
|
||||
margin-left: 0;
|
||||
>li {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
@ -318,7 +271,7 @@
|
|||
padding: 1.5rem;
|
||||
background-color: #353535;
|
||||
text-align: center;
|
||||
color: #eee;
|
||||
color: #fff;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
.dash-title {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
#dashboard-topbar {
|
||||
background-color: $light-gray;
|
||||
ul {
|
||||
background-color: $light-gray;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,10 @@
|
|||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#content-input {
|
||||
#post_content {
|
||||
min-height: 20rem;
|
||||
}
|
||||
|
||||
.content-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.preview {
|
||||
min-height: 20rem;
|
||||
border: 1px solid #DDDDDD;
|
||||
|
@ -29,7 +25,7 @@
|
|||
|
||||
#upload_photo {
|
||||
float: right;
|
||||
margin-top: 1.875rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
tr {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.admin-subscribes-field {
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.ng-dirty .ng-invalid, .ng-dirty .ng-required {
|
||||
border: 1px solid #FF7A7A !important;
|
||||
}
|
|
@ -1,25 +1,18 @@
|
|||
@import 'font-awesome-sprockets';
|
||||
@import 'font-awesome';
|
||||
@import 'foundation_and_overrides';
|
||||
@import 'foundation-icons';
|
||||
//.inner-wrap {
|
||||
//min-height: 100%;
|
||||
//}
|
||||
|
||||
@import 'aboutme_welcome';
|
||||
@import 'archives';
|
||||
@import 'foundation_and_overrides';
|
||||
|
||||
@import 'aboutme_welcome';
|
||||
@import 'archives';
|
||||
@import 'markdown';
|
||||
@import 'blogs';
|
||||
@import 'head';
|
||||
@import 'blogs';
|
||||
@import 'head';
|
||||
@import 'qrcodes';
|
||||
@import 'comments';
|
||||
@import 'angularjs';
|
||||
@import 'comments';
|
||||
@import 'highlight';
|
||||
@import 'footer';
|
||||
@import 'like_and_weixin';
|
||||
@import 'admin/*';
|
||||
|
||||
.turbolinks-progress-bar {
|
||||
height: 2px;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -11,22 +11,12 @@
|
|||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.search-result-wrapper p {
|
||||
font-size: 0.785rem;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.blog-title {
|
||||
color: #111111;
|
||||
&:hover {
|
||||
color: red;
|
||||
}
|
||||
border: none;
|
||||
|
||||
em {
|
||||
color: red;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.blog-title {
|
||||
margin-top: 1rem;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.ptag {
|
||||
|
@ -18,7 +19,7 @@
|
|||
}
|
||||
|
||||
.content {
|
||||
padding-top: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.read-more {
|
||||
|
@ -34,7 +35,7 @@
|
|||
}
|
||||
|
||||
.published-at {
|
||||
@media screen and (min-width: 40.063em) {
|
||||
@media only screen and (min-width: 40.063em) {
|
||||
text-align: right;
|
||||
}
|
||||
margin-top: 1rem;
|
||||
|
@ -44,23 +45,21 @@
|
|||
.blog-over {
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 2rem;
|
||||
border-bottom: 1px solid #DCDCDC;
|
||||
}
|
||||
|
||||
.recent-title {
|
||||
border-bottom: 1px solid #dddddd;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.recent-content {
|
||||
@media screen and (min-width: 64em) {
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
padding-bottom: 3rem;
|
||||
li {
|
||||
list-style: disc;
|
||||
}
|
||||
padding-left: 0rem;
|
||||
}
|
||||
|
||||
#qrcode-home {
|
||||
padding: 1rem 2rem 1rem 0;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
|
@ -82,11 +81,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
.social-share {
|
||||
display: none;
|
||||
}
|
||||
.subscribe-ul {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
li {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.rss-subscribe {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.wechat_qrcode {
|
||||
width: 200px;
|
||||
margin-bottom: 20px;
|
||||
.subscribe-success {
|
||||
margin-left: 1rem;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.subscribe-fail {
|
||||
margin-left: 1rem;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
#alert-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.comment-field {
|
||||
background-color: #333333;
|
||||
padding-top: 3rem;
|
||||
|
@ -47,17 +43,14 @@
|
|||
color: #b3b3b3;
|
||||
}
|
||||
.comment-content {
|
||||
padding-bottom: 1rem;
|
||||
padding-bottom: 2.275rem;
|
||||
padding-left: 0.275rem;
|
||||
border-bottom: 1px dashed #8a8a8a;
|
||||
margin-bottom: 0;
|
||||
|
||||
p {
|
||||
margin-bottom: 0.325rem;
|
||||
}
|
||||
white-space: pre;
|
||||
}
|
||||
.name {
|
||||
padding-top: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 0.275rem;
|
||||
}
|
||||
}
|
||||
|
@ -82,11 +75,11 @@
|
|||
}
|
||||
|
||||
.comment-wrapper {
|
||||
|
||||
margin-bottom: 0.5rem;
|
||||
&:hover {
|
||||
background-color: #444444;
|
||||
}
|
||||
|
||||
|
||||
.name {
|
||||
color: #DDDDDD;
|
||||
}
|
||||
|
@ -94,7 +87,8 @@
|
|||
.comment-content {
|
||||
word-wrap: break-word;
|
||||
color: #DDDDDD;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,39 +1,8 @@
|
|||
.my-topbar {
|
||||
.middle-text {
|
||||
text-align: center;
|
||||
z-index: 999;
|
||||
a {
|
||||
color: #F7F7F7;
|
||||
|
||||
&:hover {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.title-bar-title {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
#offCanvas {
|
||||
min-height: 100%;
|
||||
a {
|
||||
color: #F7F7F7;
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 3px 20px;
|
||||
background-color: #545454;
|
||||
color: #B9B9B9;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,4 +16,3 @@
|
|||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
.markdown {
|
||||
|
||||
//color: rgba(0,0,0,1);
|
||||
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (min-width: 40.063em) {
|
||||
@media only screen and (min-width: 40.063em) {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 1.875rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 40em) {
|
||||
@media only screen and (max-width: 40em) {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.2875rem;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
border-bottom: 1px solid #DDDDDD;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
|
@ -38,6 +42,8 @@
|
|||
}
|
||||
|
||||
code {
|
||||
//margin-left: 0.25rem;
|
||||
//margin-right: 0.025rem;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
#image-tag {
|
||||
float: right;
|
||||
width: 200px;
|
||||
margin-bottom: 1rem;
|
||||
.qrcode-image {
|
||||
table {
|
||||
border-width: 0;
|
||||
border-style: none;
|
||||
border-color: #0000ff;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td {
|
||||
border-width: 0;
|
||||
border-style: none;
|
||||
border-color: #0000ff;
|
||||
border-collapse: collapse;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 0.3rem;
|
||||
height: 0.3rem;
|
||||
}
|
||||
td.black { background-color: #000; }
|
||||
td.white { background-color: #fff; }
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
|
||||
module ApplicationCable
|
||||
class Channel < ActionCable::Channel::Base
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
|
||||
module ApplicationCable
|
||||
class Connection < ActionCable::Connection::Base
|
||||
end
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
|
||||
class CommentChannel < ApplicationCable::Channel
|
||||
def subscribed
|
||||
stream_from "comment_post_#{params[:post_id]}"
|
||||
end
|
||||
end
|
|
@ -10,6 +10,16 @@ class Admin::PostsController < ApplicationController
|
|||
@post = Post.find( params[:id] )
|
||||
end
|
||||
|
||||
def show
|
||||
post = Post.find( params[:id] )
|
||||
render :json=> {
|
||||
title: post.title,
|
||||
type: post.type,
|
||||
labels: post.labels_content(true),
|
||||
content: post.content
|
||||
}
|
||||
end
|
||||
|
||||
def destroy
|
||||
@post = Post.find( params[:id] )
|
||||
if @post.destroy
|
||||
|
@ -22,7 +32,7 @@ class Admin::PostsController < ApplicationController
|
|||
end
|
||||
|
||||
def index
|
||||
@posts = Post.order(created_at: :desc).page(params[:page]).per(25)
|
||||
@posts = Post.desc(:created_at)
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -61,7 +71,7 @@ class Admin::PostsController < ApplicationController
|
|||
private
|
||||
def initialize_or_create_labels(labels)
|
||||
@post.labels = []
|
||||
labels.split(",").map { |i| i.strip }.uniq.each do |name|
|
||||
labels.split(",").each do |name|
|
||||
label = Label.find_or_initialize_by(name: name.strip)
|
||||
label.save!
|
||||
@post.labels << label
|
||||
|
|
|
@ -6,18 +6,15 @@ class Admin::SessionsController < ApplicationController
|
|||
|
||||
def create
|
||||
if ENV['ADMIN_USER'].blank?
|
||||
flash.now[:alert] = t('admin.session.no_configuration')
|
||||
render :new
|
||||
render :json=> { success: false, message: t('admin.session.no_configuration') }
|
||||
elsif ENV['ADMIN_USER'] != params[:username]
|
||||
flash.now[:alert] = t('admin.session.username_error')
|
||||
render :new
|
||||
render :json=> { success: false, message: t('admin.session.username_error') }
|
||||
elsif ENV['ADMIN_PASSWORD'] != params[:password]
|
||||
flash.now[:alert] = t('admin.session.password_error')
|
||||
render :new
|
||||
render :json=> { success: false, message: t('admin.session.password_error') }
|
||||
else
|
||||
flash[:notice] = t('admin.session.login_success')
|
||||
session[:login] = true
|
||||
redirect_to admin_root_path
|
||||
render :json=> { success: true }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
class Admin::SubscribesController < ApplicationController
|
||||
layout 'layouts/admin'
|
||||
before_action :authericate_user!
|
||||
|
||||
def index
|
||||
@subscribes = Subscribe.all.order(created_at: :desc).page(params[:page]).per(25)
|
||||
end
|
||||
|
||||
def enable
|
||||
@subscribe = Subscribe.find(params[:id])
|
||||
|
||||
@subscribe.enable = true
|
||||
@subscribe.save!
|
||||
|
||||
redirect_to admin_subscribes_path, notice: "#{@subscribe.email} is enabled!"
|
||||
end
|
||||
|
||||
def disable
|
||||
@subscribe = Subscribe.find(params[:id])
|
||||
|
||||
@subscribe.enable = false
|
||||
@subscribe.save!
|
||||
|
||||
redirect_to admin_subscribes_path, notice: "#{@subscribe.email} is disabled!"
|
||||
end
|
||||
end
|
|
@ -1,10 +1,60 @@
|
|||
class ArchivesController < ApplicationController
|
||||
def index
|
||||
if (@q = params[:q]).blank?
|
||||
@posts = Post.order(created_at: :desc).page(params[:page])
|
||||
type = map[params[:type]]
|
||||
limit = 10
|
||||
start_with = params[:start_with]
|
||||
@posts = Post.desc(:created_at)
|
||||
|
||||
if type
|
||||
@posts = @posts.where(type: type)
|
||||
@type = type
|
||||
end
|
||||
|
||||
# all 与 start_with 参数同在, 说明是要获取所有start_with之前的数据
|
||||
if params[:all] and params[:start_with]
|
||||
@posts = @posts.where(:created_at.gte => Time.at(start_with.to_i))
|
||||
else
|
||||
@q_size = Post.where('title like ?', "%#{@q}%").size
|
||||
@posts = Post.where('title like ?', "%#{@q}%").order(created_at: :desc).page(params[:page])
|
||||
@posts = @posts.limit(limit)
|
||||
end
|
||||
|
||||
if !params[:all] and start_with
|
||||
@posts = @posts.where(:created_at.lt => Time.at(start_with.to_i))
|
||||
end
|
||||
|
||||
#update start_with
|
||||
unless @posts.empty?
|
||||
start_with = @posts[-1].created_at.to_i.to_s
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render :json => {
|
||||
posts: @posts.collect { |post| build_summary(post) },
|
||||
start_with: start_with
|
||||
}
|
||||
end
|
||||
format.html
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def map
|
||||
{
|
||||
"life"=> "生活",
|
||||
"tech"=> "技术",
|
||||
"creator"=> "创业"
|
||||
}
|
||||
end
|
||||
|
||||
def build_summary(post)
|
||||
{
|
||||
title: post.title,
|
||||
type: post.type_en,
|
||||
created_at: format_date(post.created_at),
|
||||
id: post.id.to_s,
|
||||
liked_count: post.liked_count,
|
||||
visited_count: post.visited_count,
|
||||
labels: post.labels_content
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
class BlogsController < ApplicationController
|
||||
|
||||
def index
|
||||
@newest = Post.order(created_at: :desc).first
|
||||
@recent = Post.order(created_at: :desc).to_a[1..3]
|
||||
@newest = Post.desc(:created_at).first
|
||||
@recent = Post.desc(:created_at).to_a[1..3]
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json
|
||||
|
@ -17,19 +17,17 @@ class BlogsController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
cookies[:cable_id] = SecureRandom.uuid
|
||||
@post = Post.find(params[:id])
|
||||
@post.visited
|
||||
@prev = Post.where('created_at < ?', @post.created_at).order(created_at: :desc).first
|
||||
@next = Post.where('created_at > ?', @post.created_at).order(created_at: :asc).first
|
||||
@comments = @post.comments.order(created_at: :desc)
|
||||
@likes_count = @post.likes.count
|
||||
@prev = Post.where(:created_at.lt => @post.created_at).desc(:created_at).where(:id.ne => @post.id).first
|
||||
@next = Post.where(:created_at.gt => @post.created_at).asc(:created_at).where(:id.ne => @post.id).first
|
||||
@comments = @post.comments
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def edit
|
||||
@post = Post.find( params[:id] )
|
||||
redirect_to edit_admin_post_path(@post)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class CommentsController < ApplicationController
|
||||
layout false
|
||||
|
||||
helper_method :format_time
|
||||
def index
|
||||
@post = Post.find( params[:blog_id] )
|
||||
res = @post.comments.desc(:created_at).collect { |comment| build_json(comment) }
|
||||
|
@ -8,33 +8,19 @@ class CommentsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
unless request.xhr?
|
||||
logger.warn "attack action detected: #{params.to_h}"
|
||||
redirect_to root_path
|
||||
return
|
||||
end
|
||||
cookies[:name] = comment_params[:name]
|
||||
cookies[:email] = comment_params[:email]
|
||||
@post = Post.find( params[:blog_id] )
|
||||
@comment = @post.comments.build(comment_params)
|
||||
comment = @post.comments.build(comment_params)
|
||||
|
||||
if @comment.save
|
||||
@comments = @post.comments.order(created_at: :desc)
|
||||
ActionCable.server.broadcast "comment_post_#{@comment.post.id}", { not: cookies[:cable_id] }
|
||||
render :create_ok
|
||||
if comment.save
|
||||
render :json=> { success: true, data: build_json(comment) }
|
||||
else
|
||||
render :create_fail
|
||||
render :json=> { success: false, message: comment.errors.full_messages.join(", ") }
|
||||
end
|
||||
end
|
||||
|
||||
def refresh
|
||||
@post = Post.find(params[:blog_id])
|
||||
@comments = @post.comments.order(created_at: :desc)
|
||||
end
|
||||
|
||||
private
|
||||
def comment_params
|
||||
params.require(:comment).permit(:content, :name, :email)
|
||||
params.permit(:content, :name, :email)
|
||||
end
|
||||
|
||||
def build_json(comment)
|
||||
|
|
|
@ -6,6 +6,15 @@ class LikesController < ApplicationController
|
|||
render :json=> { success: true, count: post.liked_count }
|
||||
end
|
||||
|
||||
def is_liked
|
||||
post = Post.find( params[:blog_id] )
|
||||
if post.likes.where(id: params[:id]).first
|
||||
render text: true
|
||||
else
|
||||
render text: false
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
post = Post.find( params[:blog_id] )
|
||||
like = post.likes.build
|
||||
|
|
|
@ -2,7 +2,7 @@ class PhotosController < ApplicationController
|
|||
def create
|
||||
@photo = Photo.new(image: params["Filedata"])
|
||||
@photo.save!
|
||||
render :plain => md_url(@photo.image.url)
|
||||
render :text=> md_url(@photo.image.url)
|
||||
end
|
||||
|
||||
def md_url(url)
|
||||
|
|
|
@ -3,23 +3,24 @@ class SubscribesController < ApplicationController
|
|||
def index
|
||||
end
|
||||
|
||||
def new
|
||||
@subscribe = Subscribe.new
|
||||
end
|
||||
|
||||
def create
|
||||
@subscribe = Subscribe.find_or_initialize_by(email: subscribe_params[:email])
|
||||
@subscribe.enable = true
|
||||
subscribe = Subscribe.find_or_initialize_by(email: params[:email])
|
||||
subscribe.enable = true
|
||||
|
||||
if @subscribe.save
|
||||
redirect_to subscribes_path, notice: '订阅成功'
|
||||
if subscribe.save
|
||||
render :json => { success: true }
|
||||
else
|
||||
render :new
|
||||
render :json => { success: false, message: subscribe.errors.full_messages.join(", ")}
|
||||
end
|
||||
end
|
||||
|
||||
def subscribe_params
|
||||
params.require(:subscribe).permit(:email)
|
||||
def cancel
|
||||
subscribe = Subscribe.find_or_initialize_by(email: params[:email])
|
||||
subscribe.enable = false
|
||||
subscribe.save
|
||||
|
||||
flash[:notice] = "退订成功: #{params[:email]}"
|
||||
render :json => { success: true }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
class UnsubscribesController < ApplicationController
|
||||
def index
|
||||
end
|
||||
|
||||
def new
|
||||
@subscribe = Subscribe.new
|
||||
end
|
||||
|
||||
def create
|
||||
subscribe = Subscribe.find_or_initialize_by(email: subscribe_params[:email])
|
||||
subscribe.enable = false
|
||||
subscribe.save
|
||||
|
||||
flash[:notice] = "退订成功: #{subscribe_params[:email]}"
|
||||
redirect_to unsubscribes_path
|
||||
end
|
||||
|
||||
def subscribe_params
|
||||
params.require(:subscribe).permit(:email)
|
||||
end
|
||||
end
|
|
@ -1,9 +1,2 @@
|
|||
module ApplicationHelper
|
||||
# some format function is defined in app/controllers/application_controller.rb
|
||||
#
|
||||
def search_highlight(title, q)
|
||||
return title if q.blank?
|
||||
|
||||
title.sub(q, "<em>#{q}</em>")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ class CommentMailer < ActionMailer::Base
|
|||
|
||||
default from: "no-reply@#{domain}"
|
||||
|
||||
def born(comment_id, to)
|
||||
def new(comment_id, to)
|
||||
@comment = Comment.find(comment_id)
|
||||
mail to: to, subject: '博主, 你的博客有新的评论'
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ class PostMailer < ActionMailer::Base
|
|||
|
||||
default from: "no-reply@#{domain}"
|
||||
|
||||
def born(post_id, to)
|
||||
def new(post_id, to)
|
||||
@post = Post.find(post_id)
|
||||
mail to: to, subject: '客官, 新博客来了'
|
||||
end
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
|
@ -1,17 +1,24 @@
|
|||
class Comment < ApplicationRecord
|
||||
class Comment
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
field :name, :type => String
|
||||
field :content, :type => String
|
||||
field :email, :type=>String
|
||||
|
||||
belongs_to :post
|
||||
validates_presence_of :post_id
|
||||
|
||||
validates :name, presence: true
|
||||
validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, message: I18n.t('comment_attributes.email') }
|
||||
validates :content, presence: true
|
||||
validates_presence_of :post_id
|
||||
|
||||
def reply_emails
|
||||
Comment.where(post_id: self.post_id).collect(&:email).uniq - [ self.email ] - Subscribe.unsubscribe_list - [ ENV['ADMIN_USER'] ]
|
||||
Comment.where(post_id: self.post_id).collect(&:email).uniq - [ self.email ] - Subscribe.unsubscribe_list
|
||||
end
|
||||
|
||||
after_commit on: :create do
|
||||
if ENV['MAIL_SERVER'].present? && ENV['ADMIN_USER'].present? && ENV['ADMIN_USER'] =~ /@/ && ENV['ADMIN_USER'] != self.email
|
||||
after_create do
|
||||
if ENV['MAIL_SERVER'].present? && ENV['ADMIN_USER'].present? && ENV['ADMIN_USER'] =~ /@/
|
||||
Rails.logger.info 'comment created, comment worker start'
|
||||
NewCommentWorker.perform_async(self.id.to_s, ENV['ADMIN_USER'])
|
||||
end
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
class Label < ApplicationRecord
|
||||
class Label
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
field :name, :type => String
|
||||
|
||||
has_and_belongs_to_many :posts
|
||||
validates :name, presence: true
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
class Like < ApplicationRecord
|
||||
class Like
|
||||
include Mongoid::Document
|
||||
|
||||
belongs_to :post
|
||||
validates_presence_of :post_id
|
||||
end
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
class Photo < ApplicationRecord
|
||||
class Photo
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
field :image
|
||||
|
||||
mount_uploader :image, PhotoUploader
|
||||
end
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
# encoding : utf-8
|
||||
#
|
||||
require 'markdown'
|
||||
class Post < ActiveRecord::Base
|
||||
class Post
|
||||
TECH = "技术"
|
||||
LIFE = "生活"
|
||||
CREATOR = "创业"
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
include Mongoid::Pagination
|
||||
field :title, :type => String
|
||||
field :content, :type => String
|
||||
field :type, :type=> String
|
||||
field :visited_count, :type=>Integer, :default=>0
|
||||
|
||||
has_many :comments
|
||||
has_and_belongs_to_many :labels
|
||||
|
||||
|
@ -7,8 +20,9 @@ class Post < ActiveRecord::Base
|
|||
|
||||
validates :title, :presence=>true, :uniqueness=> true
|
||||
validates :content, :presence=>true, :length => { :minimum=> 30 }
|
||||
validates :type, :presence=>true, :inclusion => { :in => [ TECH, LIFE, CREATOR ] }
|
||||
|
||||
after_commit on: :create do
|
||||
after_create do
|
||||
if ENV['MAIL_SERVER'].present?
|
||||
NewPostWorker.perform_async(self.id.to_s)
|
||||
end
|
||||
|
@ -24,21 +38,35 @@ class Post < ActiveRecord::Base
|
|||
md.render(content)
|
||||
end
|
||||
|
||||
def type_en
|
||||
map = {
|
||||
'技术' => 'Tech',
|
||||
'生活' => 'Life',
|
||||
'创业' => 'Creator',
|
||||
}
|
||||
|
||||
if I18n.locale == :en
|
||||
map[type]
|
||||
else
|
||||
type
|
||||
end
|
||||
end
|
||||
|
||||
def visited
|
||||
self.visited_count += 1
|
||||
self.save
|
||||
self.visited_count
|
||||
end
|
||||
|
||||
# truncate content for home page display
|
||||
# 显示给首页截断数据
|
||||
def sub_content
|
||||
HTML_Truncator.truncate(content_html, 300, length_in_chars: true)
|
||||
end
|
||||
|
||||
# truncate content for meta description display
|
||||
# 显示给 meta description
|
||||
def meta_content
|
||||
html = HTML_Truncator.truncate(content_html, 100, :length_in_chars => true, ellipsis: '')
|
||||
# Easily get text for Nokogiri
|
||||
# 加上 div 以方便 Nokogiri 获取 text()
|
||||
html = '<div>' + html + '</div>'
|
||||
Nokogiri.parse(html).text()
|
||||
end
|
||||
|
@ -52,8 +80,4 @@ class Post < ActiveRecord::Base
|
|||
def liked_count
|
||||
self.likes.size
|
||||
end
|
||||
|
||||
def liked_by?(like_id)
|
||||
!! self.likes.where(id: like_id).first
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
class Subscribe < ApplicationRecord
|
||||
class Subscribe
|
||||
include Mongoid::Document
|
||||
field :email, type: String
|
||||
field :enable, type: Mongoid::Boolean, default: true
|
||||
|
||||
validates :email, presence: true, uniqueness: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, message: '地址无效' }
|
||||
|
||||
def self.subscribe_list
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
td
|
||||
= mail_to comment.email
|
||||
td
|
||||
= simple_format(comment.content)
|
||||
p.pre #{comment.content}
|
||||
td
|
||||
= format_time(comment.created_at)
|
||||
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
= simple_form_for(@post, url: url, html: {novalidate: '' }) do |f|
|
||||
.row
|
||||
.large-6.columns
|
||||
= f.input :title, label: t('admin.posts_attributes.title'), input_html: { name: 'title' }
|
||||
= f.input :title, label: t('admin.posts_attributes.title'), "ng-model"=>"title", input_html: { name: 'title' }
|
||||
.row
|
||||
.small-6.large-3.columns
|
||||
= f.input :type, :as=>:select, :collection=> [ Post::TECH, Post::LIFE, Post::CREATOR ], label: t('admin.posts_attributes.type'), "ng-model"=>"type", input_html: { name: 'type' }
|
||||
.row
|
||||
.small-12.large-6.columns
|
||||
= label_tag :labels, t('admin.posts_attributes.labels')
|
||||
= text_field_tag :labels, @post.labels_content(true)
|
||||
= text_field_tag :labels, @post.labels_content(true), "ng-model"=>"labels", "ng-initial" => ''
|
||||
|
||||
.row
|
||||
.small-12.columns
|
||||
|
@ -14,23 +17,22 @@
|
|||
| #{t('admin.posts_attributes.already_labels')}
|
||||
span
|
||||
- Label.all.each do |label|
|
||||
a.tag href="#" #{label.name}
|
||||
a.tag href="#" ng-click="addTag($event)" #{label.name}
|
||||
|
||||
/ tabs and upload file field
|
||||
ul.tabs#tabs data-tabs=''
|
||||
li.tabs-title.is-active
|
||||
a href="#content" #{t('admin.posts_attributes.content')}
|
||||
li.tabs-title
|
||||
a href="#preview" #{t('admin.posts_attributes.preview')}
|
||||
dl.tabs
|
||||
dd ng-class="{ active: body_active }"
|
||||
a href="" ng-click="changeToBody()" #{t('admin.posts_attributes.content')}
|
||||
dd ng-class="{ active: !body_active }"
|
||||
a href="#" ng-click="changeToPreview()" #{t('admin.posts_attributes.preview')}
|
||||
= link_to t('admin.posts_attributes.upload_photo'), "#", :id=>'upload_photo'
|
||||
input[type="file" style="display: none;"]
|
||||
|
||||
.tabs-content data-tabs-content='tabs'
|
||||
.tabs-panel.content-field.is-active#content
|
||||
= f.input :content, :as=> :text, :label => false, input_html: { name: 'content', id: 'content-input' }
|
||||
.content-field ng-show="body_active" ng-model= 'content'
|
||||
= f.input :content, :as=> :text, :label => false, input_html: { name: 'content', "ng-model"=>'content', 'ng-initial'=>'' }
|
||||
|
||||
.tabs-panel.preview.markdown#preview
|
||||
.preview.markdown ng-hide="body_active" ng-bind-html=" trustAsPreviewHTML() "
|
||||
|
||||
.row
|
||||
.small-12.large-6.columns.posts-button
|
||||
button.button type='submit' #{t('admin.posts_attributes.submit')}
|
||||
button #{t('admin.posts_attributes.submit')}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
td.admin-post-summary-field
|
||||
i.fi-calendar
|
||||
span #{format_time(post.created_at)}
|
||||
i.fi-list
|
||||
span #{ post.type_en }
|
||||
i.fi-pricetag-multiple
|
||||
span #{ post.labels_content }
|
||||
i.fi-torsos
|
||||
|
@ -25,4 +27,3 @@
|
|||
= link_to t('comment'), admin_post_comments_path(post.id), class: 'edit-post-link'
|
||||
= link_to t('edit'), edit_admin_post_path(post), class: 'edit-post-link'
|
||||
= link_to t('destroy'), admin_post_path(post), method: 'DELETE', 'data-confirm' => '确认删除?'
|
||||
== paginate @posts
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
.row ng-controller="AdminSessionsController"
|
||||
.small-12.large-8.columns
|
||||
h3.blog-title #{t('admin.session.title')}
|
||||
= form_tag admin_sessions_path
|
||||
form ng-submit="login()"
|
||||
.row
|
||||
.small-12.large-8.columns
|
||||
= label_tag 'username', t('admin.session.username')
|
||||
= text_field_tag 'username', params[:username], placeholder: t('admin.session.username_placeholder')
|
||||
= text_field_tag 'username', nil, placeholder: t('admin.session.username_placeholder'), "ng-model"=>"username"
|
||||
= label_tag 'username', t('admin.session.password')
|
||||
= password_field_tag 'password', params[:password], placeholder: t('admin.session.password_placeholder')
|
||||
= password_field_tag 'password', nil, placeholder: t('admin.session.password_placeholder'), "ng-model"=>"password"
|
||||
|
||||
button.button type='submit' #{t('admin.session.login_button')}
|
||||
p
|
||||
.alert-box.warning ng-show=" error_msg "
|
||||
|{{ error_msg }}
|
||||
|
||||
button #{t('admin.session.login_button')}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
.row.admin-subscribes-field
|
||||
.small-12.columns
|
||||
h3.blog-title #{t('admin.subscribes') }
|
||||
table width="100%"
|
||||
thead
|
||||
tr
|
||||
th #{t('admin.subscribes_head.email')}
|
||||
th #{t('admin.subscribes_head.enable')}
|
||||
th #{t('admin.subscribes_head.created_at')}
|
||||
th #{t('admin.subscribes_head.operation')}
|
||||
tbody
|
||||
- @subscribes.each do |subscribe|
|
||||
tr
|
||||
td
|
||||
= mail_to subscribe.email
|
||||
td
|
||||
= subscribe.enable
|
||||
td
|
||||
= format_time(subscribe.created_at)
|
||||
td
|
||||
- if subscribe.enable
|
||||
= link_to t('admin.subscribes_head.op.disable'), disable_admin_subscribe_path(subscribe), method: 'POST', data: { confirm: 'Confirm?' }
|
||||
- else
|
||||
= link_to t('admin.subscribes_head.op.enable'), enable_admin_subscribe_path(subscribe), method: 'POST', data: { confirm: 'Confirm?' }
|
||||
== paginate @subscribes
|
|
@ -1,29 +1,30 @@
|
|||
- content_for(:title) do
|
||||
| #{t('title.timeline')}
|
||||
.row
|
||||
| #{@type ? @type : t('title.timeline')}
|
||||
.row ng-controller="ArchivesController" ng-cloak=""
|
||||
.small-12.large-9.large-centered.columns
|
||||
ul.archives-field
|
||||
.search-wrapper
|
||||
= form_with url: archives_path, method: 'GET' do |f|
|
||||
= f.search_field :q, value: @q, placeholder: t('archive.search')
|
||||
- @posts.each do |post|
|
||||
li
|
||||
= link_to blog_path(post), class: 'blog-title' do
|
||||
== search_highlight(post.title, @q)
|
||||
p.tags-field
|
||||
i.fi-calendar
|
||||
span
|
||||
= format_date(post.created_at)
|
||||
i.fi-pricetag-multiple
|
||||
span
|
||||
= post.labels_content
|
||||
i.fi-torsos
|
||||
span
|
||||
= post.visited_count
|
||||
i.fi-heart
|
||||
span
|
||||
= post.liked_count
|
||||
- if @q.present?
|
||||
.search-result-wrapper
|
||||
p.text-muted 共 #{@q_size || 0} 条结果
|
||||
= paginate @posts, q: @q
|
||||
ul.archives-field ng-model="type" ng-init=" type= '#{@type}' "
|
||||
li ng-repeat=" post in posts "
|
||||
a.blog-title ng-href="{{ visit(post.id) }}"
|
||||
|{{ post.title }}
|
||||
p.tags-field
|
||||
i.fi-calendar
|
||||
span
|
||||
|{{ post.created_at }}
|
||||
i.fi-list-thumbnails
|
||||
span
|
||||
|{{ post.type }}
|
||||
i.fi-pricetag-multiple
|
||||
span
|
||||
|{{ post.labels }}
|
||||
i.fi-torsos
|
||||
span
|
||||
|{{ post.visited_count }}
|
||||
i.fi-heart
|
||||
span
|
||||
|{{ post.liked_count }}
|
||||
.no-more-field
|
||||
p ng-show="no_more_flag" #{t('nocontent')}
|
||||
|
||||
.load-more
|
||||
button.small ng-click="load()" ng-show="!loading_flag" Load More
|
||||
button.small ng-show="loading_flag" Loading
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
.row
|
||||
.row ng-controller="CommentsController" ng-cloak=""
|
||||
.small-12.large-9.large-centered.columns
|
||||
= form_for Comment.new, url: blog_comments_path(@post), remote: true do |f|
|
||||
form novalidate='' name='form'
|
||||
.row
|
||||
.small-12.large-12.columns
|
||||
= f.text_area :content, placeholder: t('comment_placeholder.content')
|
||||
= text_area_tag(:content, nil, placeholder: t('comment_placeholder.content'), 'ng-model'=> 'content', 'ng-required'=> true)
|
||||
.row
|
||||
.small-12.large-6.columns
|
||||
= f.text_field :name, value: cookies[:name], placeholder: t('comment_placeholder.name')
|
||||
= f.text_field :email, value: cookies[:email], placeholder: t('comment_placeholder.email')
|
||||
button.button.comment-submit type='submit' data-disable-with=t('comment_placeholder.submitting') #{t('comment_placeholder.submit')}
|
||||
#alert-container.alert.callout
|
||||
span.text
|
||||
button class="close-button" type='button'
|
||||
span ×
|
||||
= render partial: 'comments/comment_content', locals: { comments: comments }
|
||||
= text_field_tag(:name, nil, placeholder: t('comment_placeholder.name'), 'ng-model'=> 'name', 'ng-required'=> 'true')
|
||||
= text_field_tag(:email, nil, placeholder: t('comment_placeholder.email'), 'ng-model'=> 'email', 'ng-pattern'=>"/^.+@.+$/", 'ng-required'=>"true")
|
||||
button.comment-submit ng-click="submit()" ng-disabled="form.$invalid || submitting" {{ submitting && "#{t('comment_placeholder.submitting')}" || "#{t('comment_placeholder.submit')}" }}
|
||||
p.comment-success ng-show="publish_success" #{t('comment_placeholder.publish_success')}
|
||||
p.comment-fail ng-show="publish_success == false" #{t('comment_placeholder.publish_fail')}: {{ publish_fail_msg }}
|
||||
.comment-diag
|
||||
.comment-wrapper ng-repeat=" comment in comments "
|
||||
p.name
|
||||
|{{ comment.name + " • " }}
|
||||
span.created-at
|
||||
|{{ comment.created_at }}
|
||||
/ ignore "white-space: pre" 's effect
|
||||
<p class=comment-content>{{ comment.content }}</p>
|
||||
|
|
|
@ -8,16 +8,21 @@ p.ptag.published-at
|
|||
span #{format_date(post.created_at)}
|
||||
|
||||
= render 'common/copyright'
|
||||
hr.blog-over
|
||||
p
|
||||
button.button.like-button class="#{'liked' if post.liked_by?(cookies[:like])}" type='button' data-url=blog_likes_path(post)
|
||||
span.count #{@likes_count}
|
||||
span Like
|
||||
.qrcode
|
||||
a#qrcode-link href="#"
|
||||
i.fi-link
|
||||
| #{t('qr_code')}
|
||||
|
||||
.social-share
|
||||
.qrcode-wrapper
|
||||
= render partial: "qrcode", locals: { str: blog_url(post) }
|
||||
hr.blog-over
|
||||
p ng-controller="LikesController" ng-cloak=""
|
||||
button.like-button ng-show="! is_liked " ng-click="submit()"
|
||||
|{{ count }}
|
||||
span Like
|
||||
button.like-button.liked ng-show=" is_liked " ng-click="cancel()"
|
||||
|{{ count }}
|
||||
span Like
|
||||
div ng-controller = "QRCodesController"
|
||||
.qrcode
|
||||
a href="#" ng-model="qrcode" ng-init="qrcode=false" ng-click="show()"
|
||||
i.fi-link
|
||||
| #{t('qr_code')}
|
||||
|
||||
.social-share ng-show='qrcode'
|
||||
.qrcode-wrapper
|
||||
= render partial: "qrcode", locals: { str: blog_url(post) }
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/ require: locals: { post : post }
|
||||
h2.blog-title #{post.title}
|
||||
p.ptag
|
||||
span
|
||||
i.fi-list-thumbnails
|
||||
span #{post.type_en}
|
||||
span
|
||||
i.fi-pricetag-multiple
|
||||
span #{post.labels_content}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.qrcode-image
|
||||
#image-tag data-url=str
|
||||
= image_tag( qrcodes_path(str: str) )
|
||||
p #{t('qrcodetips')}
|
||||
|
|
|
@ -15,16 +15,32 @@
|
|||
= link_to t('home.read'), blog_path(@newest), class: 'read-more'
|
||||
p.published-at #{t('home.created_at')} #{format_date(@newest.created_at)}
|
||||
|
||||
- if @recent.present?
|
||||
h4.recent-title #{t('home.recent')}
|
||||
ul.recent-content
|
||||
- @recent.each do |re|
|
||||
li = link_to re.title,blog_path(re)
|
||||
h4.recent-title #{t('home.recent')}
|
||||
ul.recent-content
|
||||
- @recent.each do |re|
|
||||
li = link_to "#{re.title}",blog_path(re)
|
||||
|
||||
.large-3.columns.large-offset-1.self-introduce.self-introduce-index
|
||||
/*Adjust it in common/welcome*/
|
||||
= render 'common/welcome'
|
||||
h4 #{t('subscribes.title')}
|
||||
.row
|
||||
.row.ng-cloak ng-controller='AboutController'
|
||||
.small-12.medium-6.large-12.columns
|
||||
= image_tag 'wechat_qrcode.jpg', class: 'wechat_qrcode'
|
||||
ul.subscribe-ul
|
||||
- if ENV['MAIL_SERVER'].present?
|
||||
li
|
||||
= link_to t('subscribes.email'), '', "ng-click"=>"click('email')"
|
||||
.email-subscribe ng-show="type == 'email'"
|
||||
= text_field_tag 'email', nil, placeholder: 'your@email.com', 'ng-model'=>'email'
|
||||
button.small ng-click="subscribe()" ng-disabled="! email_validate()" #{t('subscribes.submit')}
|
||||
span.subscribe-success ng-show="subscribe_success" #{t('subscribes.submit_success')}
|
||||
span.subscribe-fail ng-show="subscribe_success == false" {{subscribe_fail_msg}}
|
||||
li
|
||||
= link_to t('subscribes.wechat'), '', "ng-click"=>"click('weixin')"
|
||||
.weixin-subscribe ng-show="type == 'weixin'"
|
||||
= render partial: "qrcode", locals: { str: root_url }
|
||||
li
|
||||
= link_to t('subscribes.rss'), '', "ng-click"=>"click('rss')"
|
||||
.rss-subscribe ng-show="type == 'rss'"
|
||||
= link_to rss_blogs_path do
|
||||
- image_tag('rss.png')
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
- content_for(:meta) do
|
||||
meta name="description" content=@post.meta_content
|
||||
meta name="description" content="#{@post.meta_content}"
|
||||
meta name="keywords" content=@post.labels_content
|
||||
|
||||
- content_for(:title) do
|
||||
| #{@post.title}
|
||||
.row.blog-wrapper#blog-show-page data-url=refresh_blog_comments_path(@post) data-post_id=@post.id
|
||||
.row.blog-wrapper
|
||||
.small-12.large-9.large-centered.columns
|
||||
= render partial: "post", :locals=> { post: @post }
|
||||
= render partial: "post", :locals=> { :post=> @post }
|
||||
.comment-field
|
||||
= render partial: 'comment', locals: { comments: @comments, post: @post }
|
||||
p
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
- if @prev
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
p
|
||||
| 你的博客有新的评论:
|
||||
p
|
||||
span 来自:
|
||||
span 来自:
|
||||
= mail_to @comment.email, @comment.name
|
||||
p 评论内容:
|
||||
p 评论内容:
|
||||
pre
|
||||
| #{@comment.content}
|
||||
|
||||
p
|
||||
| 博客链接:
|
||||
p
|
||||
| 博客链接:
|
||||
= link_to @comment.post.title, blog_url(@comment.post)
|
|
@ -1,11 +1,11 @@
|
|||
p 你评论过的博客有新的回复:
|
||||
|
||||
p 评论人: #{@comment.name}
|
||||
p 评论内容:
|
||||
|
||||
p 评论内容:
|
||||
|
||||
pre #{@comment.content}
|
||||
|
||||
p
|
||||
| 被评论博客:
|
||||
| 被评论博客:
|
||||
= link_to @comment.post.title, blog_url(@comment.post)
|
||||
hr
|
||||
p
|
||||
| 退订:
|
||||
= link_to unsubscribes_url, unsubscribes_url
|
||||
|
|
|
@ -7,5 +7,3 @@
|
|||
被评论博客: <%= @comment.post.title %>
|
||||
|
||||
访问这里回复: <%= blog_url(@comment.post) %>
|
||||
|
||||
退订: <%= unsubscribes_url %>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.comment-diag
|
||||
- comments.each do |comment|
|
||||
.comment-wrapper
|
||||
p.name
|
||||
| #{comment.name}
|
||||
| #{" • "}
|
||||
span.created-at
|
||||
| #{format_time(comment.created_at) }
|
||||
= render partial: 'comments/comment_pre', locals: { comment: comment }
|
|
@ -1,2 +0,0 @@
|
|||
.comment-content
|
||||
= simple_format(comment.content)
|
|
@ -1,2 +0,0 @@
|
|||
$('#alert-container .text').text(' <%= @comment.errors.full_messages.join(' ') %>' );
|
||||
$('#alert-container').removeClass('success').addClass('alert').show();
|
|
@ -1,4 +0,0 @@
|
|||
$('#comment_content').val('');
|
||||
$('#alert-container .text').text(' <%= t('comment_placeholder.publish_success') %>' );
|
||||
$('#alert-container').removeClass('alert').addClass('success').show();
|
||||
$('.comment-diag').replaceWith('<%= j render partial: 'comment_content', locals: { comments: @comments }%>');
|
|
@ -1 +0,0 @@
|
|||
$('.comment-diag').replaceWith('<%= j render partial: 'comment_content', locals: { comments: @comments }%>');
|
|
@ -0,0 +1,23 @@
|
|||
h2.blog-title ABOUT ME
|
||||
p 我的名字是李亚飞( YaFei Lee ), 网名为 WinDy, 由于是我第一个网名, 所以一直使用至今.
|
||||
p 我是 85 后成员, 目前生活在深圳, 计划长期在这里发展.
|
||||
p 当年毕业于吉林大学计算机专业, 并在深信服工作约 5 年时间, 如今正在独立创业路中.
|
||||
p 曾经对软件测试开发领域有浓厚兴趣, 如今对产品开发与公司运营兴趣较多. 平时多研究 Ruby on Rails, Web 开发等技术. 闲暇之余爱好中国象棋, 乒乓球, 读书.
|
||||
p 最近正在读的书在这里: <a href='http://book.douban.com/people/41759170/collect'>豆瓣书单</a>
|
||||
|
||||
h4 近期动态
|
||||
p 目前在独立创业路中, 正在互联网金融大浪中淘金.
|
||||
p WinDy's Blog 开博也有 2 年以上了, 由于个人原因, 博客完全自主采用 Ruby on Rails 完成, 在 2014 年 3 月进行了重构, 以移动设备优先进行了设计.
|
||||
p 闲暇之余, 开通了微信公众账号, 与 WinDy's Blog 不同, 这里不讲技术性内容, 主要面向生活感悟, 个人思考等, 欢迎关注.
|
||||
= image_tag 'weixin.jpg'
|
||||
p 也可以搜索 <strong>技术达人李亚飞</strong> 来找到我.
|
||||
p 我对微信是重度使用者, 几乎不使用微博, 微信账号可以在我个别博客里发现, Enjoying!
|
||||
|
||||
h4 更多链接
|
||||
ul
|
||||
li
|
||||
a href="https://github.com/windy" target="_blank" Github
|
||||
li
|
||||
a href="http://www.douban.com/people/41759170/" target="_blank" Douban
|
||||
li
|
||||
a href="http://www.linkedin.com/pub/yafei-lee/77/3b/505" target="_blank" LinkedIn
|
|
@ -1,6 +1,6 @@
|
|||
/* 样式调整请找 stylesheet: .self-introduce-index */
|
||||
h4 欢迎
|
||||
p 我是技术达人李亚飞
|
||||
p 我是李亚飞, WinDy 是我的网名.
|
||||
|
||||
h4 关于我
|
||||
ul.aboutme-index
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- if (1.days.from_now).strftime('%-m-%-d') =~ /^1-[123]$/
|
||||
.new-year
|
||||
.callout.primary data-closable=''
|
||||
.alert-box data-alert=''
|
||||
| 我的朋友, 祝你元旦快乐
|
||||
button.close-button type='button' data-close='' ×
|
||||
a href='#' class='close' ×
|
||||
|
|
|
@ -1,29 +1,25 @@
|
|||
- content_for(:title) do
|
||||
| #{t('title.about')}
|
||||
- content_for(:main) do
|
||||
.about-page
|
||||
.top-bar-wrapper.contain-to-grid.fixed
|
||||
nav#about-top-bar.top-bar
|
||||
.top-bar-left
|
||||
ul.menu
|
||||
li
|
||||
a.name href='/' Back to WinDy Blog
|
||||
.top-bar-right
|
||||
ul.menu
|
||||
li
|
||||
.responsive-button data-responsive-toggle="responsive-menu" data-hide-for="medium"
|
||||
button class="menu-icon" type="button" data-toggle=''
|
||||
#responsive-menu
|
||||
.top-bar-right
|
||||
ul.menu
|
||||
.about-page ng-app='app' ng-controller='AboutScrollController'
|
||||
.top-bar-wrapper.contain-to-grid.fixed ng-class="{ active: ! is_top() }"
|
||||
.row
|
||||
.small-12.columns
|
||||
nav.top-bar data-topbar='' role='navigation'
|
||||
ul.title-area
|
||||
li.name
|
||||
h1
|
||||
a href='/' Back to WinDy's Blog
|
||||
section.top-bar-section
|
||||
ul.right
|
||||
li
|
||||
a href='#about' ABOUT
|
||||
a href='#about' du-smooth-scroll='' du-scrollspy='' ABOUT
|
||||
li
|
||||
a href='#skill' SKILL
|
||||
a href='#skill' du-smooth-scroll='' du-scrollspy='' SKILL
|
||||
li
|
||||
a href='#work' PORTFOLIO
|
||||
a href='#work' du-smooth-scroll='' du-scrollspy='' PORTFOLIO
|
||||
li
|
||||
a href='#contact' CONTACT
|
||||
a href='#contact' du-smooth-scroll='' du-scrollspy='' CONTACT
|
||||
#intro
|
||||
header.intro
|
||||
.intro-heading
|
||||
|
@ -36,13 +32,13 @@
|
|||
| Find a loved job rather than found a good job.
|
||||
br
|
||||
| Technology advance NOT by human's intelligence, but by their laziness.
|
||||
a.circle href='#about'
|
||||
a.circle href='#about' du-smooth-scroll=''
|
||||
i.fa.fa-angle-double-down
|
||||
section#about
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title About Me
|
||||
p I'm Li yafei, located on the city of Shenzhen, in China. I'm a Ruby on Rails full-stack Developer.
|
||||
p I'm Li yafei, located on Shenzhen, China. I'm a Ruby on Rails full-stack Developer.
|
||||
p
|
||||
| I graduated from Jinlin university at 2009, and then I worked for
|
||||
a href='http://sangfor.com'   Sangfor Corporation  
|
||||
|
@ -50,16 +46,11 @@
|
|||
p
|
||||
| At March 2014, I start a startup company named Cywin( Chinese name: 创业赢) with my partner.
|
||||
p
|
||||
a href='http://cywin.yafeilee.me' Cywin
|
||||
'
|
||||
| is an investment crowdfunding platform for startups and investors which was built by me alone in six month.
|
||||
| Cywin.cn is an investment crowdfunding platform for startups and investors which was built by me alone in six month.
|
||||
p
|
||||
| But it failed at 2014.10 because of the lack of related resource.
|
||||
| Then it failed because the lack of resource below the line.
|
||||
p
|
||||
| After that, I research remote work and jump in, I built a remote-work team called 80percent, which help people building nice websites, mobile apps, and so on.
|
||||
p
|
||||
' You can follow us at:
|
||||
a href='http://80percent.io' 80percent website
|
||||
| After that, I try to research and attempt remote work, I build a new Chinese remote team called 80%, which help people building a nice website, a mobile app, and etc.
|
||||
section#skill
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
|
@ -70,10 +61,11 @@
|
|||
li Ruby on Rails( expert )
|
||||
li Linux / OSX( expert )
|
||||
li Git( expert )
|
||||
li AngularJS / React / VueJS / jQuery ( skilled )
|
||||
li AngularJS( skilled )
|
||||
li Bootstrap / Foundation 5( skilled )
|
||||
li HTML5 / CSS3
|
||||
li Agile Development
|
||||
li jQuery( skilled )
|
||||
li HTML5
|
||||
li CSS3
|
||||
li Testing Automation
|
||||
li Deploying Automation
|
||||
li PostgreSQL / Mysql / Mongodb
|
||||
|
@ -82,43 +74,46 @@
|
|||
.small-12.large-9.large-centered.columns
|
||||
h1.title Portfolio
|
||||
ul.works
|
||||
li
|
||||
span.time 2015.3 - 2016.x
|
||||
.project
|
||||
span.name Lina
|
||||
span.brief an API provider based on Ruby on Rails
|
||||
span.link
|
||||
a href='http://linarb.org' target='_blank' http://linarb.org
|
||||
ul.project-description
|
||||
li Auto-generated API docs.
|
||||
li zero learning costs for Railser.
|
||||
li Qiniu cloud storage, Wechat Oauth, Alipay integration.
|
||||
li
|
||||
span.time 2014.12 - 2015.3
|
||||
.project
|
||||
span.name Jiaoluo
|
||||
span.brief A MOOC education online system
|
||||
span.link
|
||||
a href='http://jiaoluo.yafeilee.me' target='_blank' http://jiaoluo.yafeilee.me
|
||||
a href='http://jiaoluo.it' target='_blank' http://jiaoluo.it
|
||||
ul.project-description
|
||||
li Build it alone for a friend in two month.
|
||||
li Awful cool modern UI.
|
||||
li Primary features: Wechat Scan Login, Alipay payment, Video HTML5, community system.
|
||||
li Based on Ruby on Rails, using Turoblinks & RJS for ajax.
|
||||
li Qiniu cloud storage( like aws storage ), Wechat Oauth ( like Facebook oauth ), Alipay integration( like paypal ).
|
||||
li Qiniu cloud storage, Wechat Oauth, Alipay integration.
|
||||
li
|
||||
span.time 2014.12 - 2015.2
|
||||
.project
|
||||
span.name Lina
|
||||
span.brief A RESTful API provider based on Ruby on Rails
|
||||
span.link
|
||||
a href='http://linarb.org' target='_blank' http://linarb.org( Chinese only )
|
||||
ul.project-description
|
||||
li An awesome API framework just like Grape.
|
||||
li Lina can help you generate APIDOC, zero-learning costs for Railser.
|
||||
li Lina is more powerful than Grape because it is by standing upon the shoulders of Giants.
|
||||
li
|
||||
| For more information, goto:
|
||||
a href='http://linarb.org' target='_blank' http://linarb.org( Chinese only )
|
||||
li
|
||||
span.time 2014.3 - 2014.10
|
||||
.project
|
||||
span.name Cywin
|
||||
span.brief an investment crowdfunding platform for startups and investors
|
||||
span.link
|
||||
a href='http://cywin.yafeilee.me' target='_blank' http://cywin.yafeilee.me
|
||||
a href='http://cywin.cn' target='_blank' http://cywin.cn
|
||||
ul.project-description
|
||||
li I build it myself, using stack of Ruby on Rails, AngularJS, Foundation 5.
|
||||
li A lot of feature: login/auth system, notification, state machine, sunspot search, background job, logic code is large 50+ controllers, 120+ testcases.
|
||||
li cost six month
|
||||
li
|
||||
' The code was open-source at March 2015, you can find it from
|
||||
| The code was open-source at March 2015, you can find it from
|
||||
a href='https://github.com/windy/cywin' target='_blank' github
|
||||
| .
|
||||
li
|
||||
|
@ -129,7 +124,7 @@
|
|||
span.link
|
||||
a href='https://github.com/windy/wblog' target='_blank' https://github.com/windy/wblog
|
||||
ul.project-description
|
||||
li A modern blog system based on Ruby on Rails 5.0.
|
||||
li A modern blog system
|
||||
li Responsive page, like, share, comment system, dashboard.
|
||||
li
|
||||
span.time 2012 - 2013.10
|
||||
|
@ -146,9 +141,9 @@
|
|||
.small-12.large-9.large-centered.columns
|
||||
h1.title Contact
|
||||
p
|
||||
| I'm working on building nice web app & native app for clients. Please contact me if you need help.
|
||||
| I'm working for building nice webapp & native app for you. Please contact me if you need help.
|
||||
p
|
||||
| If you are a developer, I also would like to talk with you about technology trend and so on.
|
||||
| If you are a developer, I also would like to talk with you about technology direct and sth.
|
||||
p
|
||||
| Please feel free to contact me.
|
||||
p.mail_to
|
||||
|
@ -167,8 +162,8 @@
|
|||
i.fa.fa-twitter
|
||||
| Twitter
|
||||
p.modified-at
|
||||
| Updated at 2016.04.28
|
||||
| Updated at 2015.4.8
|
||||
.footer
|
||||
.row
|
||||
.small-12.columns
|
||||
div Copyright © 2012 - 2016 en.yafeilee.me
|
||||
div Copyright © 2012 - 2015 en.yafeilee.me
|
||||
|
|
|
@ -1,116 +1,93 @@
|
|||
- content_for(:title) do
|
||||
| #{t('title.about')}
|
||||
- content_for(:main) do
|
||||
.about-page
|
||||
.top-bar-wrapper.contain-to-grid.fixed
|
||||
nav#about-top-bar.top-bar
|
||||
.top-bar-left
|
||||
ul.menu
|
||||
li
|
||||
a.name href='/' 回到博客
|
||||
.top-bar-right
|
||||
ul.menu
|
||||
li
|
||||
.responsive-button data-responsive-toggle="responsive-menu" data-hide-for="medium"
|
||||
button class="menu-icon" type="button" data-toggle=''
|
||||
#responsive-menu
|
||||
.top-bar-right
|
||||
ul.menu
|
||||
li
|
||||
a href='#about' 关于
|
||||
li
|
||||
a href='#skill' 技能
|
||||
li
|
||||
a href='#work' 作品
|
||||
li
|
||||
a href='#contact' 联系
|
||||
.about-page ng-app='app' ng-controller='AboutScrollController'
|
||||
.top-bar-wrapper.contain-to-grid.fixed ng-class="{ active: ! is_top() }"
|
||||
.row
|
||||
.small-12.columns
|
||||
nav.top-bar data-topbar='' role='navigation'
|
||||
ul.title-area
|
||||
li.name
|
||||
h1
|
||||
a href='/' 回到博客
|
||||
section.top-bar-section
|
||||
ul.right
|
||||
li
|
||||
a href='#about' du-smooth-scroll='' du-scrollspy='' 关于
|
||||
li
|
||||
a href='#skill' du-smooth-scroll='' du-scrollspy='' 技能
|
||||
li
|
||||
a href='#work' du-smooth-scroll='' du-scrollspy='' 作品
|
||||
li
|
||||
a href='#contact' du-smooth-scroll='' du-scrollspy='' 联系
|
||||
#intro
|
||||
header.intro
|
||||
.intro-heading
|
||||
.row
|
||||
.small-12.columns
|
||||
h1.heading 李亚飞
|
||||
a.version href='http://en.yafeilee.me/about' English Version
|
||||
.sub-heading
|
||||
p 懒,驱动人类进步的关键。
|
||||
a.circle href='#about'
|
||||
p
|
||||
| 懒, 是人类进步的动力
|
||||
br
|
||||
| 所谓技术, 就是让你的生活越来越懒
|
||||
a.circle href='#about' du-smooth-scroll=''
|
||||
i.fa.fa-angle-double-down
|
||||
section#about
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title 关于我
|
||||
p 我是李亚飞,坐标深圳,连续创业者,技术达人,有众多开源项目和多场技术分享,联合创立 3 家技术产品驱动的公司,前单麦科技联合创始人&CTO,现至简天成科技CEO。
|
||||
.wrapper
|
||||
.time 2019.03 ~ 现在
|
||||
p 前公司已被某C轮公司收购,再次创立了至简天成科技,一家为创业者提供产品孵化的技术型公司。
|
||||
ul
|
||||
li 我们选择靠谱有潜力的创业公司。
|
||||
li 创新的持股+现金的技术合作模式。
|
||||
li 为创业伙伴打磨产品,利益共享长期发展。
|
||||
li 现已有发展不错的创业合作伙伴。
|
||||
.wrapper
|
||||
.time 2016.04 ~ 2018.12
|
||||
p 深圳百分之八十网络技术有限公司联合创始人 & CTO,旗下主要产品单麦小程序平台是帮助商家一键制作小程序的SAAS服务平台,我与另一位联合创始人一道创立并运营了整个公司与产品,我的主要职责包括共同决策产品方向,负责研发团队日常管理,参与市场部销售计划制定与支持工作。
|
||||
ul
|
||||
li 组建以敏捷开发,全栈工程师为核心的高效率研发团队
|
||||
li 带领团队连续 52 次版本迭代,每周发布新版本
|
||||
li 单麦小程序平台客户体验满意度90%以上,上线小程序几千家,服务用户15万+
|
||||
li 单麦小程序平台成为微信小程序生态 TOP10 获奖者
|
||||
.wrapper
|
||||
.time 2015
|
||||
p SmartX 创始员工,SmartX 是位于中国的全球领先的超融合存储方案供应商,利用软件技术方案帮助企业大规模降低存储硬件的成本。作为早期创始员工,担任前端架构师角色,顺利保证 SmartX OS 的快速交付。
|
||||
.wrapper
|
||||
.time 2014
|
||||
p Cywin.cn 联合创始人 & 技术负责人,Cywin.cn 是一家股权众筹平台,对标美国的 Angelist.co,帮助创业公司完成股权融资的交易平台。作为项目的发起人之一,负责产品的研发工作。
|
||||
.wrapper
|
||||
.time 2009 ~ 2014
|
||||
p 深信服(已上市)是一家著名的 “华为系” 网络公司。加入时研发规模600人,作为当时研发部成长最快的工程师之一,担任自动化产品线的主管兼技术负责人,带领技术团队进行质量管理方面的工作。
|
||||
|
||||
p 我是李亚飞, 一个在中国深圳的全栈开发工程师( Full Stack Developer ).
|
||||
p
|
||||
| 曾经在
|
||||
a href='http://sangfor.com' 深信服
|
||||
| 工作大约 5 年( 2009.6 - 2014.3 ). 在那里, 从一个菜鸟成长为一个资深工程师, 还有幸带领一个很酷的团队帮助公司进行自动化测试方向的研究与推进.
|
||||
p
|
||||
| 在 2014 年 3 月份, 与一个很不错的合伙人一起, 他负责业务, 我负责技术开发了创业赢(http://cywin.cn), 这是一个股权众筹平台, 帮助创业团队更好的融资, 但是在 10 月份的时候项目快速失败了.
|
||||
p
|
||||
| 2015 年 3 月, 我与几个伙伴正式组建了
|
||||
a href='http://80percent.io' 80%
|
||||
| 远程办公团队, 专注于为创业团队提供技术外包服务.
|
||||
p
|
||||
| 几个月来, 帮助了好几个项目( 天天投资365, 鲜品汇, 要有光等 )的成长.
|
||||
p
|
||||
| 2015 年 7 月, 重新加入了一家新的创业公司
|
||||
a href='http://www.smartx.com' smartx
|
||||
| , 目前作为前端全栈工程师入伙, 它有可能在未来二年成为中国另一个独角兽.
|
||||
p
|
||||
| 下一步, 未来请等待...
|
||||
section#skill
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title 开发技能栈
|
||||
h1.title 开发技能
|
||||
.skills
|
||||
ul
|
||||
li Ruby on Rails ( 精通 )
|
||||
li Linux / OSX ( 非常熟悉 )
|
||||
li Git / Svn ( 非常熟悉 )
|
||||
li AngularJS / React / VueJS / ES6 / Jquery ( 非常熟悉 )
|
||||
li Bootstrap / Foundation 6 ( 非常熟悉 )
|
||||
li HTML5 ( 熟悉 )
|
||||
li CSS3 ( 熟悉 )
|
||||
li Agile Development
|
||||
li Ruby on Rails( 熟练 )
|
||||
li Linux / OSX( 熟练 )
|
||||
li Git / Svn( 熟练 )
|
||||
li AngularJS( 熟练 )
|
||||
li Bootstrap / Foundation 5( 熟练 )
|
||||
li jQuery( 熟练 )
|
||||
li HTML5
|
||||
li CSS3
|
||||
li Testing Automation
|
||||
li Deploying Automation
|
||||
li PostgreSQL / Mysql / Mongodb
|
||||
section#work
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title 个人主要作品
|
||||
h1.title 主要作品
|
||||
ul.works
|
||||
li
|
||||
span.time 2015.3 - 2016.x
|
||||
.project
|
||||
span.name Lina
|
||||
span.brief 一个专注于 API 接口编写的框架
|
||||
span.link
|
||||
a href='https://github.com/windy/lina' target='_blank' https://github.com/windy/lina
|
||||
ul.project-description
|
||||
li 自动生成文档
|
||||
li 零学习成本, 集成 Rails 的 API 开发最佳实践
|
||||
li 自动校验参数, 快速构建安全可靠的 API
|
||||
li
|
||||
' Github 地址:
|
||||
a href='https://github.com/windy/lina' https://github.com/windy/lina
|
||||
li
|
||||
span.time 2014.12 - 2015.3
|
||||
.project
|
||||
span.name 青角落
|
||||
span.brief 有趣的互联网人, 知识
|
||||
span.link
|
||||
a href='http://jiaoluo.yafeilee.me' target='_blank' http://jiaoluo.yafeilee.me
|
||||
a href='http://jiaoluo.it' target='_blank' http://jiaoluo.it
|
||||
ul.project-description
|
||||
li 朋友的创业项目, 独立开发
|
||||
li 现代的功能: 微信登录, 支付系统, 视频托管, 用户社区.
|
||||
li 帮助朋友独立开发, 现代感极强的 UI 及 功能: 微信扫一扫, 支付宝, 视频托管, 社区.
|
||||
li Ruby on Rails 架构, 使用 Turoblinks, RJS 进行前端交互.
|
||||
li 七牛云存储集成, 微信集成, 支付宝集成.
|
||||
li
|
||||
|
@ -119,12 +96,12 @@
|
|||
span.name Cywin
|
||||
span.brief 一个股权众筹的商业平台
|
||||
span.link
|
||||
a href='https://github.com/windy/cywin' target='_blank' https://github.com/windy/cywin
|
||||
a href='http://cywin.yafeilee.me' target='_blank' http://cywin.yafeilee.me
|
||||
ul.project-description
|
||||
li 全栈独立负责整个项目的后端, 前端, 架构在 Ruby on Rails, AngularJS, Foundation 5.
|
||||
li 花费周期 6 个月, 上线.
|
||||
li
|
||||
' 代码在 2015 年 3 月已经
|
||||
| 代码在 2015 年 3 月已经
|
||||
a href='https://github.com/windy/cywin' target='_blank' 开源
|
||||
| .
|
||||
li
|
||||
|
@ -138,7 +115,7 @@
|
|||
li Ruby on Rails 开源博客系统, 帮助更多的朋友构建高定制性的博客.
|
||||
li 具备博客管理, 点赞, 评论, 二维码, 自适应等现代网站所有特性.
|
||||
li
|
||||
span.time 2011.x - 2013.10
|
||||
span.time 2012 - 2013.10
|
||||
.project
|
||||
span.name ATM/ATT
|
||||
span.brief 测试业界领先的关键字驱动的自动化测试平台
|
||||
|
@ -153,18 +130,11 @@
|
|||
.small-12.large-9.large-centered.columns
|
||||
h1.title 联系我
|
||||
p
|
||||
| 对于技术开发者, 我可以帮助你指导前端开发技术发展的趋势预测, 给予职业发展规划.
|
||||
br
|
||||
| 对于创业者, 我擅长于精益创业分析, 帮助你快速落地项目, 少走技术弯路.
|
||||
| 如果你正在创业或在创业规划中, 我也许可以提供一些技术性建议, 欢迎与我交流
|
||||
p
|
||||
| 如果你是开发者, 可以找我谈谈技术发展, 程序员的生活, 或许能帮助更好的规划职业发展.
|
||||
| 如果你想与我谈谈技术, 生活, 也可以与我随时与我交流
|
||||
p
|
||||
| 如果你是创业者, 可以找我谈谈你的创业规划, 技术困难, 我将尽力帮助你.
|
||||
p
|
||||
' 也欢迎到我们的公司主页了解更多信息:
|
||||
a href='http://www.dao42.com' 至简天成官网
|
||||
p
|
||||
| 保持放松, 请随时邮件与我联系
|
||||
| 不要紧张, 请随时联系我
|
||||
p.mail_to
|
||||
= mail_to 'lyfi2003@gmail.com'
|
||||
ul.contact-ul
|
||||
|
@ -182,8 +152,8 @@
|
|||
| 豆
|
||||
| Douban
|
||||
p.modified-at
|
||||
| 本页更新于 2019.03.29
|
||||
| 本页更新于 2015.7.7
|
||||
.footer
|
||||
.row
|
||||
.small-12.columns
|
||||
div Copyright © 2012 - 2019 yafeilee.me
|
||||
div Copyright © 2012 - 2016 yafeilee.me
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/ Non-link tag that stands for skipped pages...
|
||||
- available local variables
|
||||
current_page : a page object for the currently displayed page
|
||||
total_pages : total number of pages
|
||||
per_page : number of items to fetch per page
|
||||
remote : data-remote
|
||||
li.ellipsis
|
|
@ -1,10 +0,0 @@
|
|||
/ Link to the "Last" page
|
||||
- available local variables
|
||||
url : url to the last page
|
||||
current_page : a page object for the currently displayed page
|
||||
total_pages : total number of pages
|
||||
per_page : number of items to fetch per page
|
||||
remote : data-remote
|
||||
span.last
|
||||
== link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, :remote => remote
|
||||
'
|
|
@ -1,10 +0,0 @@
|
|||
/ Link to the "Next" page
|
||||
- available local variables
|
||||
url : url to the next page
|
||||
current_page : a page object for the currently displayed page
|
||||
total_pages : total number of pages
|
||||
per_page : number of items to fetch per page
|
||||
remote : data-remote
|
||||
li.pagination-next class="#{'disabled' if current_page.last?}"
|
||||
== link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, :rel => 'next', :remote => remote
|
||||
'
|