diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..295778a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore Byebug command history file. +.byebug_history + +# Ignore application configuration +/config/application.yml + diff --git a/.rspec b/.rspec new file mode 100644 index 000000000..83e16f804 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..acc93211e --- /dev/null +++ b/Gemfile @@ -0,0 +1,85 @@ +source 'https://rubygems.org' + +gem 'delayed_job_active_record' + +gem 'figaro' + +gem 'aws-sdk' + +gem 'paperclip' + +gem 'rails-controller-testing' + +gem 'factory_girl_rails' + +gem 'rspec-rails' + +gem 'will_paginate' + +gem 'carrierwave' + +gem 'better_errors' + +gem 'hirb' + +gem 'bootstrap' + +gem 'bootstrap-sass' + +gem 'binding_of_caller' + +gem 'faker' + +gem 'pg' + +gem 'selenium-webdriver' + +gem 'bcrypt' +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.0.0' +# Use sqlite3 as the database for Active Record +#gem 'sqlite3' +# Use Puma as the app server +gem 'puma', '~> 3.0' +# Use SCSS for stylesheets +gem 'sass-rails', '~> 5.0' +# Use Uglifier as compressor for JavaScript assets +gem 'uglifier', '>= 1.3.0' +# Use CoffeeScript for .coffee assets and views +gem 'coffee-rails', '~> 4.2' +# See https://github.com/rails/execjs#readme for more supported runtimes +# gem 'therubyracer', platforms: :ruby + +# Use jquery as the JavaScript library +gem 'jquery-rails' +# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.5' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 3.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platform: :mri + gem 'shoulda-matchers' + gem 'capybara' + gem 'launchy' + gem 'letter_opener' +end + +group :development do + # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. + gem 'web-console' + gem 'listen', '~> 3.0.5' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..cb0cf8458 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,295 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.0.0.1) + actionpack (= 5.0.0.1) + nio4r (~> 1.2) + websocket-driver (~> 0.6.1) + actionmailer (5.0.0.1) + actionpack (= 5.0.0.1) + actionview (= 5.0.0.1) + activejob (= 5.0.0.1) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.0.0.1) + actionview (= 5.0.0.1) + activesupport (= 5.0.0.1) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.0.0.1) + activesupport (= 5.0.0.1) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + activejob (5.0.0.1) + activesupport (= 5.0.0.1) + globalid (>= 0.3.6) + activemodel (5.0.0.1) + activesupport (= 5.0.0.1) + activerecord (5.0.0.1) + activemodel (= 5.0.0.1) + activesupport (= 5.0.0.1) + arel (~> 7.0) + activesupport (5.0.0.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.4.0) + arel (7.1.1) + autoprefixer-rails (6.4.0.2) + execjs + aws-sdk (2.5.4) + aws-sdk-resources (= 2.5.4) + aws-sdk-core (2.5.4) + jmespath (~> 1.0) + aws-sdk-resources (2.5.4) + aws-sdk-core (= 2.5.4) + bcrypt (3.1.11) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) + bootstrap (4.0.0.alpha3.1) + autoprefixer-rails (>= 6.0.3) + sass (>= 3.4.19) + bootstrap-sass (3.3.7) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) + builder (3.2.2) + byebug (9.0.5) + capybara (2.8.0) + addressable + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + carrierwave (0.11.2) + activemodel (>= 3.2.0) + activesupport (>= 3.2.0) + json (>= 1.7) + mime-types (>= 1.16) + mimemagic (>= 0.3.0) + childprocess (0.5.9) + ffi (~> 1.0, >= 1.0.11) + climate_control (0.0.3) + activesupport (>= 3.0) + cocaine (0.5.8) + climate_control (>= 0.0.3, < 1.0) + coderay (1.1.1) + coffee-rails (4.2.1) + coffee-script (>= 2.2.0) + railties (>= 4.0.0, < 5.2.x) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.10.0) + concurrent-ruby (1.0.2) + debug_inspector (0.0.2) + delayed_job (4.1.2) + activesupport (>= 3.0, < 5.1) + delayed_job_active_record (4.1.1) + activerecord (>= 3.0, < 5.1) + delayed_job (>= 3.0, < 5) + diff-lcs (1.2.5) + erubis (2.7.0) + execjs (2.7.0) + factory_girl (4.7.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.7.0) + factory_girl (~> 4.7.0) + railties (>= 3.0.0) + faker (1.6.6) + i18n (~> 0.5) + ffi (1.9.14) + figaro (1.1.1) + thor (~> 0.14) + globalid (0.3.7) + activesupport (>= 4.1.0) + hirb (0.7.3) + i18n (0.7.0) + jbuilder (2.6.0) + activesupport (>= 3.0.0, < 5.1) + multi_json (~> 1.2) + jmespath (1.3.1) + jquery-rails (4.1.1) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + json (2.0.2) + launchy (2.4.3) + addressable (~> 2.3) + letter_opener (1.4.1) + launchy (~> 2.2) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + loofah (2.0.3) + nokogiri (>= 1.5.9) + mail (2.6.4) + mime-types (>= 1.16, < 4) + method_source (0.8.2) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mimemagic (0.3.2) + mini_portile2 (2.1.0) + minitest (5.9.0) + multi_json (1.12.1) + nio4r (1.2.1) + nokogiri (1.6.8) + mini_portile2 (~> 2.1.0) + pkg-config (~> 1.1.7) + paperclip (5.0.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + cocaine (~> 0.5.5) + mime-types + mimemagic (~> 0.3.0) + pg (0.18.4) + pkg-config (1.1.7) + puma (3.6.0) + rack (2.0.1) + rack-test (0.6.3) + rack (>= 1.0) + rails (5.0.0.1) + actioncable (= 5.0.0.1) + actionmailer (= 5.0.0.1) + actionpack (= 5.0.0.1) + actionview (= 5.0.0.1) + activejob (= 5.0.0.1) + activemodel (= 5.0.0.1) + activerecord (= 5.0.0.1) + activesupport (= 5.0.0.1) + bundler (>= 1.3.0, < 2.0) + railties (= 5.0.0.1) + sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.1) + actionpack (~> 5.x) + actionview (~> 5.x) + activesupport (~> 5.x) + rails-dom-testing (2.0.1) + activesupport (>= 4.2.0, < 6.0) + nokogiri (~> 1.6.0) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + railties (5.0.0.1) + actionpack (= 5.0.0.1) + activesupport (= 5.0.0.1) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (11.2.2) + rb-fsevent (0.9.7) + rb-inotify (0.9.7) + ffi (>= 0.5.0) + rspec-core (3.5.2) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-rails (3.5.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + rubyzip (1.2.0) + sass (3.4.22) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + selenium-webdriver (2.53.4) + childprocess (~> 0.5) + rubyzip (~> 1.0) + websocket (~> 1.0) + shoulda-matchers (3.1.1) + activesupport (>= 4.0.0) + spring (1.7.2) + spring-watcher-listen (2.0.0) + listen (>= 2.7, < 4.0) + spring (~> 1.2) + sprockets (3.7.0) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.1.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + thor (0.19.1) + thread_safe (0.3.5) + tilt (2.0.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + uglifier (3.0.1) + execjs (>= 0.3.0, < 3) + web-console (3.3.1) + actionview (>= 5.0) + activemodel (>= 5.0) + debug_inspector + railties (>= 5.0) + websocket (1.2.3) + websocket-driver (0.6.4) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + will_paginate (3.1.0) + xpath (2.0.0) + nokogiri (~> 1.3) + +PLATFORMS + ruby + +DEPENDENCIES + aws-sdk + bcrypt + better_errors + binding_of_caller + bootstrap + bootstrap-sass + byebug + capybara + carrierwave + coffee-rails (~> 4.2) + delayed_job_active_record + factory_girl_rails + faker + figaro + hirb + jbuilder (~> 2.5) + jquery-rails + launchy + letter_opener + listen (~> 3.0.5) + paperclip + pg + puma (~> 3.0) + rails (~> 5.0.0) + rails-controller-testing + rspec-rails + sass-rails (~> 5.0) + selenium-webdriver + shoulda-matchers + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + uglifier (>= 1.3.0) + web-console + will_paginate + +BUNDLED WITH + 1.12.5 diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..e85f91391 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 000000000..b16e53d6d --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/images/default_cover.jpg b/app/assets/images/default_cover.jpg new file mode 100644 index 000000000..94a8abeff Binary files /dev/null and b/app/assets/images/default_cover.jpg differ diff --git a/app/assets/images/default_image.jpg b/app/assets/images/default_image.jpg new file mode 100644 index 000000000..3d660d77d Binary files /dev/null and b/app/assets/images/default_image.jpg differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100644 index 000000000..9ee69eefc Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 000000000..2a92ffeea --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,21 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require jquery +//= require jquery_ujs +//= require_tree . + +$(document).ready(function() { + Comments.findLastComment(); + Comments.findComment(); + Posts.listToDelete(); +}) \ No newline at end of file diff --git a/app/assets/javascripts/cable.js b/app/assets/javascripts/cable.js new file mode 100644 index 000000000..71ee1e66d --- /dev/null +++ b/app/assets/javascripts/cable.js @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the rails generate channel command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/app/assets/javascripts/channels/.keep b/app/assets/javascripts/channels/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/javascripts/comments.js b/app/assets/javascripts/comments.js new file mode 100644 index 000000000..3c8fbfe0d --- /dev/null +++ b/app/assets/javascripts/comments.js @@ -0,0 +1,27 @@ +var Comments = (function() { + + var findComment = function() { + $("body").on("click", ".delete-comment", function(e) { + var commentId = ($(this).data("comment-id")) + deleteComment(commentId); + }) + } + + var findLastComment = function () { + $("body").on("click", ".comment-section", function(e) { + var postId = ($(this).data("post-id")); + var lastComment = $(".comment-show[data-post-id=" + postId + "]").last() + //console.log(lastComment); + }) + } + + var deleteComment = function(id) { + $(".comment-show[data-comment-id=" + id + "]").remove(); + } + + return { + findComment: findComment, + findLastComment: findLastComment + } + +})(); \ No newline at end of file diff --git a/app/assets/javascripts/likes.coffee b/app/assets/javascripts/likes.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/likes.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/photos.coffee b/app/assets/javascripts/photos.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/photos.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js new file mode 100644 index 000000000..5b31bb357 --- /dev/null +++ b/app/assets/javascripts/posts.js @@ -0,0 +1,47 @@ +var Posts = (function() { + + var getPost = function() { + + $("#new_post").submit(function(e) { + e.preventDefault(); + + var data = e.target + var _$post = $('textarea[name="post[content]"]').val(); + + $ajaxSubmitPost(_$post) + }) + } + + var listToDelete = function(){ + $("body").on("click", ".delete-section", function(e) { + e.preventDefault(); + var id = $(this).data("post-id"); + var $div = $(".col-md-12[data-post-id=" + id + "]"); + deleteThis($div); + }) + } + + var deleteThis = function(thing) { + thing.remove(); + } + + var ajaxSubmitPost = function(post) { + $.ajax({ + url: "/posts", + method: "POST", + data: JSON.stringify({ post: post }), + contentType: "application/json", + dataType: "json", + success: function() { + console.log("DA") + } + }); + }; + +return { + getPost: getPost, + listToDelete: listToDelete, + deleteThis: deleteThis +} + +})(); diff --git a/app/assets/javascripts/profile.coffee b/app/assets/javascripts/profile.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/profile.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/relationships.coffee b/app/assets/javascripts/relationships.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/relationships.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/sessions.coffee b/app/assets/javascripts/sessions.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/sessions.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/timelines.coffee b/app/assets/javascripts/timelines.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/timelines.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 000000000..420a0462a --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,289 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ + +body { + background-color: #dfe3ee; + font-family: "Helvetica"; +} + +/* USERS/NEW */ + + .log-in-navbar { + background-color: #3b5998; + + } + + .log-in-button { + background-color: #1E90FF; + margin-top:25px; + margin-left:-40px; + + } + +.log-in-information { + float:right; + display:inline-block; + margin-top:10px; + +} + +.danebook-text { + color:white; +} + +.remember-me { + margin-top:25px; +} + +.danebook { + color:white; + text-decoration: none; +} + +.logged-in-text { + display:inline-block; + color:white; + font-weight: bold; +} + +.connect-information-container { + +} + +.connect-information { + margin-top:50px; +} + +.connect-information-point { + margin-top:30px; + color:grey; + font-size:20px; +} + +.sign-up-field { + margin-top: 40px; +} + +.danebook-logo { + float:left; + margin-left:-70px; + color:white; + border: 4px solid white; + padding:0px 10px 0px 0px; +} + + +.search-input { + width:65%; + vertical-align: center; +} + + +/* USER/:id/PROFILES */ + +.cover-photo { + width:90%; + height:300px; + display:block; + margin:auto; + margin-top:-20px; +} + +.profile-photo { + position:absolute; + margin-top:61px; + height:35%; + width:15%; +} + +.profile-navigation { + border: 2px solid black; + +} + +.timeline-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#A8A8A8; + } +} + +.about-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#A8A8A8; + } +} + +.photos-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#A8A8A8; + } +} + +.friends-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#A8A8A8; + } +} + +.edit-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + color:#3b5998; + transition: all 0.5s ease; + &:hover { + background-color:#A8A8A8 ; + } +} + +.about-user-section { + background-color: #A8A8A8 ; + text-align: center; + border:2px solid black; + font-size:30px; + margin-top:2em; +} + +.basic-and-words-to-live { + margin-bottom: 10px; +} + +.basic { + border: 2px solid black +} + +.words-to-live { + border: 2px solid black +} + +.about-edit-link { + float:right; + text-decoration:none; +} + +.submit-edit-button { + margin-bottom:50px +} + +.text-area-about { + resize:none; +} + +.new-post-text-area { + resize:none; +} + +.post-container { + +} + +.post-button { + margin:0; + display:inline; +} + +.about-details { + float:left; +} + +.panel-body { + word-wrap: break-word; +} + +.post-photo { + height:100px; + width:100px; +} + +.displayed-post { + margin-bottom:20px; + margin-top:20px; +} + +.comment-picture { + height:50px; + width:50px; +} + +.comment-text { + display:inline-block; +} + +.index-photo { + padding:20px; +} + +.no-photos { + color: #3b5998; +} + +.resize-index-photo { + height:100px; + width:100px; +} +.comment-section { + display:inline-block; +} + +.like-section { + display:inline-block; +} + +.delete-section { + display:inline-block; +} + +.show-photo { + display:block; + margin: 0 auto; +} + +.upload-photo-form { + margin: 0 auto; + display: block; +} + +.photo-in-index { + transition: all 0.5s ease; + padding:20px; + &:hover { + background-color: #3b5998; + } +} +.same-line { + display:inline-block; +} \ No newline at end of file diff --git a/app/assets/stylesheets/comments.scss b/app/assets/stylesheets/comments.scss new file mode 100644 index 000000000..3722c124e --- /dev/null +++ b/app/assets/stylesheets/comments.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the comments controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/likes.scss b/app/assets/stylesheets/likes.scss new file mode 100644 index 000000000..1f08d329f --- /dev/null +++ b/app/assets/stylesheets/likes.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the likes controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/photos.scss b/app/assets/stylesheets/photos.scss new file mode 100644 index 000000000..1a3e082cd --- /dev/null +++ b/app/assets/stylesheets/photos.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the photos controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/posts.scss b/app/assets/stylesheets/posts.scss new file mode 100644 index 000000000..1a7e15390 --- /dev/null +++ b/app/assets/stylesheets/posts.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the posts controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/profile.scss b/app/assets/stylesheets/profile.scss new file mode 100644 index 000000000..6644935a2 --- /dev/null +++ b/app/assets/stylesheets/profile.scss @@ -0,0 +1,27 @@ +// Place all the styles related to the Profile controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ + + +.about-section { + background-color:white; + margin-bottom:20px; + border: 1px solid #DCDCDC; +} + + +.photo-section { + background-color:white; + margin-bottom:20px; + border: 1px solid #DCDCDC; +} + +.friend-section { + background-color:white; + border: 1px solid #DCDCDC; +} + +.post-section { + background-color:white; + border: 2px solid #DCDCDC; +} diff --git a/app/assets/stylesheets/relationships.scss b/app/assets/stylesheets/relationships.scss new file mode 100644 index 000000000..6302c66ef --- /dev/null +++ b/app/assets/stylesheets/relationships.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the relationships controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 000000000..7bef9cf82 --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/timelines.scss b/app/assets/stylesheets/timelines.scss new file mode 100644 index 000000000..92f39ca74 --- /dev/null +++ b/app/assets/stylesheets/timelines.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the timelines controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 000000000..b9aee0d51 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,4 @@ +// Place all the styles related to the users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ + diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 000000000..d67269728 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 000000000..0ff5442f4 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 000000000..21897f962 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,66 @@ +class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + + + private + + def both_friends?(user) + current_user ? current_user.friends.include?(user) && user.friends.include?(current_user) : false + end + + def set_user + @user = User.find_by_id(params[:user_id]) + redirect_to root_path, :flash=> {:danger => "Unable to find that user"} unless @user + end + + #Put this in user controller + def set_user_user_controller + @user = User.find(params[:id]) + redirect_to root_path, :flash=> {:danger => "Unable to find that user"} unless @user + end + + def sign_in(user) + user.regenerate_auth_token + cookies[:auth_token] = user.auth_token + @current_user = user + end + + def permanent_sign_in(user) + user.regenerate_auth_token + cookies.permanent[:auth_token] = user.auth_token + @current_user = user + end + + def sign_out + @current_user = nil + cookies.delete(:auth_token) + end + + def current_user + @current_user ||= User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token] + end + + helper_method :current_user + + def signed_in_user? + !!current_user + end + + helper_method :signed_in_user? + + def require_login + unless signed_in_user? + flash[:danger] = "You are not logged in. Please login." + redirect_to new_user_path + end + end + + def require_current_user + unless current_user + flash[:danger] = "You're not allowed there. Nice try." + redirect_to "http://nouveller.com/404/" + end + end + + +end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 000000000..76dc75353 --- /dev/null +++ b/app/controllers/comments_controller.rb @@ -0,0 +1,60 @@ +class CommentsController < ApplicationController + def new + @comment = Comment.new + end + + def create + parent = params[:commentable] + parent_id = (parent.downcase << "_id").to_sym + @commentable = parent.classify.constantize.find(params[parent_id]) + @comment = @commentable.comments.build(comment_params) + @comment.user = User.find_by_id(@comment.commentable.user_id) + @comment.from = current_user.id + if @comment.save + flash[:success] = "Your comment has been saved!" + respond_to do |format| + format.js{} + format.html {redirect_back(fallback_location: root_path)} + end + else + flash[:danger] = "Your comment needs to have content in it" + respond_to do |format| + format.js{} + format.html {redirect_back(fallback_location: root_path)} + end + end + end + + def destroy + @comment = Comment.find_by_id(params[:id]) + if @comment.destroy + respond_to do |format| + format.html{redirect_to current_user} + format.js{} + end + end + + end + + + def index + end + + def show + end + + + private + + def comment_params + params.require(:comment).permit(:content, :from) + end + + # def underscore + # self.gsub(/::/, '/'). + # gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). + # gsub(/([a-z\d])([A-Z])/,'\1_\2'). + # tr("-", "_"). + # downcase + # end +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb new file mode 100644 index 000000000..61c9cb3e7 --- /dev/null +++ b/app/controllers/likes_controller.rb @@ -0,0 +1,24 @@ +class LikesController < ApplicationController + def create + parent = params[:likeable] + parent_id = (parent.downcase << "_id").to_sym + @likeable = parent.classify.constantize.find(params[parent_id]) + @like = @likeable.likes.build(likes_params) + @like.from = current_user.id + if !Like.already_liked?(@likeable, current_user) && @like.save! + flash[:success] = "Liked!" + redirect_back(fallback_location: root_path) + else + flash[:danger] = "You already liked that" + redirect_back(fallback_location: root_path) + end + end + + def destroy + end + + + def likes_params + params.require(:like).permit(:user_id, :from) + end +end diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb new file mode 100644 index 000000000..ec343eff1 --- /dev/null +++ b/app/controllers/photos_controller.rb @@ -0,0 +1,44 @@ +class PhotosController < ApplicationController + before_action :require_current_user, only: [:destroy, :create] + + + def index + @user = User.find_by_id(params[:user_id]) + @photos = @user.photos + unless current_user || both_friends?(@user) + flash[:danger] = "You have to be friends with them to view their photos" + redirect_back(fallback_location: root_path) + end + end + + def create + @user = User.find_by_id(params[:user_id]) + @photo = @user.photos.build(photo_params) unless !params[:photo] + if @photo && @photo.save + flash[:success] = "Your photo has been added!" + redirect_to user_photos_path + else + flash[:danger] = "You need to choose a photo to upload" + redirect_to user_photos_path + end + end + + def destroy + end + + def new + + end + + def show + @user = User.find_by_id(params[:user_id]) + @photo = Photo.find(params[:id]) + end + + private + + def photo_params + params.require(:photo).permit(:picture, :user_id) + end + +end \ No newline at end of file diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb new file mode 100644 index 000000000..fd6c335a9 --- /dev/null +++ b/app/controllers/posts_controller.rb @@ -0,0 +1,44 @@ +class PostsController < ApplicationController + before_action :require_current_user, only: [:destroy, :create] + before_action :set_user, only: [:create, :destroy] + + + def create + @post = @user.posts.build(post_params) + @post.from = current_user.id + if @post.save + flash[:success] = "Your post as been successfully posted for all to see!" + respond_to do |format| + format.html {redirect_to @user} + format.js {} + end + + else + flash[:danger] = "You post was not posted" + respond_to do |format| + format.html {redirect_to current_user} + format.js {} + end + + end + end + + def destroy + @post = Post.find_by_id(params[:id]) + if @post.destroy + respond_to do |format| + format.html{redirect_to current_user} + format.js{} + end + end + + end + + + private + + def post_params + params.require(:post).permit(:content, :from, comments_attributes: [:content]) + end + +end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb new file mode 100644 index 000000000..0909bfe7c --- /dev/null +++ b/app/controllers/profiles_controller.rb @@ -0,0 +1,26 @@ +class ProfilesController < ApplicationController + before_action :require_current_user, only: [:edit] + before_action :set_user, only: [:show, :edit] + + def edit + if @user != current_user + redirect_to "http://nouveller.com/404/" + else + @profile = current_user.profile + @photo = current_user.photos.build + end + end + + def update + end + + def show + @profile = @user.profile + end + + def destroy + end + + def index + end +end diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb new file mode 100644 index 000000000..f888f1861 --- /dev/null +++ b/app/controllers/relationships_controller.rb @@ -0,0 +1,37 @@ +class RelationshipsController < ApplicationController + before_action :signed_in_user?, only: [:create, :destroy] + before_action :current_user, only: [:create, :destroy] + + def index + @user = current_user + @friends = current_user.friends + end + + def create + @user = User.find(params[:friended_id]) + if current_user.friended_relationships.create(friended_id: @user.id) + flash[:success] = "You are now friends with #{@user.name}" + redirect_to user_profiles_path(@user) + else + flash[:error] = "You will never friend #{@user.name}!" + redirect_to user_profiles_path(@user) + end + end + + def destroy + @relationship1 = Relationship.find(params[:id]) + @user = User.find(@relationship1.friended_id) + @relationship2 = Relationship.where(friended_id: current_user.id).where( friender_id: @user.id) + if @relationship2.empty? + flash[:success] = "Friend request deleted" + @relationship1.destroy + redirect_to user_profiles_path(@user) + else + flash[:success] = "You are no longer friends with #{@user.name}" + @relationship2.first.destroy + @relationship1.destroy + redirect_to user_profiles_path(@user) + end + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 000000000..a8fb2a8f2 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,24 @@ +class SessionsController < ApplicationController + + def create + @user = User.find_by_email(params[:email]) + if @user && @user.authenticate(params[:password]) + if params[:remember_me] + permanent_sign_in(@user) + else + sign_in(@user) + end + flash[:success] = "You've successfully signed into Danebook!" + redirect_to current_user + else + flash[:danger] = "We did not sign you into Danebook. Please provide a valid email and password" + redirect_to root_url + end + end + + def destroy + sign_out + flash[:success] = "You've successfully signed out" + redirect_to root_url + end +end \ No newline at end of file diff --git a/app/controllers/timelines_controller.rb b/app/controllers/timelines_controller.rb new file mode 100644 index 000000000..6133aa383 --- /dev/null +++ b/app/controllers/timelines_controller.rb @@ -0,0 +1,2 @@ +class TimelinesController < ApplicationController +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 000000000..0d7ca82a4 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,61 @@ +class UsersController < ApplicationController + # before_action :set_user_user_controller, only: [:edit, :update, :destroy] + # before_action :require_login, except: [:new, :create, :show] + # before_action :require_current_user, only: [:edit, :update, :destroy] + + def new + @user = User.new + end + + def index + @users = User.search(params[:search]) + end + + def edit + + end + + def update + if current_user.update(user_params) + flash[:success] = "Your Danebook profile has been updated" + redirect_to user_profiles_path(current_user) + else + flash[:danger] = "Failed to update your Danebook profile" + + current_user.errors.full_messages.join(", ") + redirect_to user_profiles_path(current_user) + end + end + + def create + @user = User.new(user_params) + if @user.save + sign_in(@user) + flash[:success] = "Welcome to Danebook!" + redirect_to user_profiles_path(current_user) + else + flash.now[:danger] = "Please fill out all fields and make sure your password and password confirmation match" + render :new + end + end + + def destroy + end + + def show + @user = User.find(params[:id]) + @profile = @user.profile + @post = current_user.posts.build if current_user + @comment = @post.comments.build if current_user + @like = @post.likes.build if current_user + @posts = @user.all_posts + @photos = Photo.newest_six(@user) + end + + private + + def user_params + params.require(:user).permit(:email, :first_name, :last_name, :password, :password_confirmation, + profile_attributes: [:about_me, :words_to_live_by, :telephone, :current_location, :hometown, :college, :id, :birthday]) + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 000000000..de6be7945 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb new file mode 100644 index 000000000..0ec9ca5f2 --- /dev/null +++ b/app/helpers/comments_helper.rb @@ -0,0 +1,2 @@ +module CommentsHelper +end diff --git a/app/helpers/likes_helper.rb b/app/helpers/likes_helper.rb new file mode 100644 index 000000000..a78a75964 --- /dev/null +++ b/app/helpers/likes_helper.rb @@ -0,0 +1,2 @@ +module LikesHelper +end diff --git a/app/helpers/photos_helper.rb b/app/helpers/photos_helper.rb new file mode 100644 index 000000000..0a10d472b --- /dev/null +++ b/app/helpers/photos_helper.rb @@ -0,0 +1,2 @@ +module PhotosHelper +end diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb new file mode 100644 index 000000000..a7b8cec89 --- /dev/null +++ b/app/helpers/posts_helper.rb @@ -0,0 +1,2 @@ +module PostsHelper +end diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb new file mode 100644 index 000000000..5a0d6b31f --- /dev/null +++ b/app/helpers/profile_helper.rb @@ -0,0 +1,2 @@ +module ProfileHelper +end diff --git a/app/helpers/relationships_helper.rb b/app/helpers/relationships_helper.rb new file mode 100644 index 000000000..3b96a9c02 --- /dev/null +++ b/app/helpers/relationships_helper.rb @@ -0,0 +1,2 @@ +module RelationshipsHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 000000000..309f8b2eb --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/helpers/timelines_helper.rb b/app/helpers/timelines_helper.rb new file mode 100644 index 000000000..1f1a4f076 --- /dev/null +++ b/app/helpers/timelines_helper.rb @@ -0,0 +1,2 @@ +module TimelinesHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 000000000..7000e191a --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,8 @@ +module UsersHelper + + def from_user(post) + user = User.find_by_id(post.from) + user.first_name + " " + user.last_name + end + +end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 000000000..a009ace51 --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 000000000..286b2239d --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 000000000..e8616435b --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,28 @@ +class UserMailer < ApplicationMailer + default from: "welcome@danebook.com" + + def welcome(user) + @user = user + mail(to: @user.email, subject: 'Welcome to Danebook!') + end + + def commented_by(commenter, to_user, object) + @user = to_user + @commenter = commenter + @object = object + mail(to: @user.email, subject: "#{@commenter.first_name} just commented on a #{@object.class.to_s.downcase} of yours!") + end + + def liked_by(liker, to_user, object) + @user = to_user + @liker = liker + @object = object + mail(to: @user.email, subject: "#{@liker.first_name} just liked a #{@object.class.to_s.downcase} of yours!") + end + + def posted_by(poster, to_user, object) + @user = to_user + @poster = poster + mail(to: @user.email, subject: "#{@poster.first_name} just posted on your wall!") + end +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 000000000..10a4cba84 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 000000000..bd6967323 --- /dev/null +++ b/app/models/comment.rb @@ -0,0 +1,20 @@ +class Comment < ApplicationRecord + belongs_to :user + belongs_to :commentable, polymorphic: true + has_many :likes, as: :likeable + has_many :comments, as: :commentable, dependent: :nullify + + validates :content, presence: true + + #after_create :commented_by_email + + accepts_nested_attributes_for :comments + + def commented_by_email + commenter = User.find_by_id(self.from) + the_user_id = self.commentable.respond_to?(:from) ? self.commentable.from : self.commentable.user_id + to_user = User.find_by_id(the_user_id) + commentable = self.commentable + UserMailer.commented_by(commenter, to_user, commentable).deliver! unless commenter == to_user + end +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/models/like.rb b/app/models/like.rb new file mode 100644 index 000000000..a30cbb224 --- /dev/null +++ b/app/models/like.rb @@ -0,0 +1,9 @@ +class Like < ApplicationRecord + belongs_to :likeable, polymorphic: true + belongs_to :user + + def self.already_liked?(post, user) + post.likes.pluck(:user_id).include?(user.id) + end + +end diff --git a/app/models/photo.rb b/app/models/photo.rb new file mode 100644 index 000000000..1038876ee --- /dev/null +++ b/app/models/photo.rb @@ -0,0 +1,18 @@ +class Photo < ApplicationRecord + + has_attached_file :picture, :styles => { :medium => "300x300", :thumbnail => "100x100" } + validates_attachment_content_type :picture, :content_type => /\Aimage\/.*\Z/ + + has_one :cover_photo, class_name: "Photo", foreign_key: :cover_photo_id + has_one :profile_photo, class_name: "Photo", foreign_key: :profile_photo_id + has_many :comments, as: :commentable + has_many :likes, as: :likeable + + belongs_to :user + + + def self.newest_six(user) + user.photos.order("created_at DESC").take(6) + end + +end diff --git a/app/models/post.rb b/app/models/post.rb new file mode 100644 index 000000000..b5e252b30 --- /dev/null +++ b/app/models/post.rb @@ -0,0 +1,13 @@ +class Post < ApplicationRecord + belongs_to :user + has_many :comments, as: :commentable + has_many :likes, as: :likeable + validates :user_id, presence: true + validates :content, presence: true, length: { minimum: 1 } + + accepts_nested_attributes_for :comments + + def all_comments + self.comments.order("created_at DESC") + end +end diff --git a/app/models/profile.rb b/app/models/profile.rb new file mode 100644 index 000000000..d865697e2 --- /dev/null +++ b/app/models/profile.rb @@ -0,0 +1,10 @@ +class Profile < ApplicationRecord + belongs_to :user + + belongs_to :cover_photo, class_name: "Photo", foreign_key: :cover_photo_id, optional: true + belongs_to :profile_photo, class_name: "Photo", foreign_key: :profile_photo_id, optional: true + + + + +end diff --git a/app/models/relationship.rb b/app/models/relationship.rb new file mode 100644 index 000000000..ea9ce921f --- /dev/null +++ b/app/models/relationship.rb @@ -0,0 +1,4 @@ +class Relationship < ApplicationRecord + belongs_to :friender, class_name: "User" + belongs_to :friended, class_name: "User" +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..abf64f667 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,59 @@ +class User < ApplicationRecord + has_many :posts + has_one :profile, dependent: :nullify + has_many :photos + has_many :likes + has_many :friended_relationships, class_name: "Relationship", + foreign_key: "friender_id", + dependent: :destroy + has_many :been_friended_relationships, class_name: "Relationship", + foreign_key: "friended_id", + dependent: :destroy + has_many :friends, through: :friended_relationships, source: :friended + has_many :been_friended, through: :been_friended_relationships, source: :friender + + before_create :generate_token + after_create :create_profile + #after_create :welcome_email + has_secure_password + + validates :password, + :length => { :in => 5..20 }, + :allow_nil => true + validates :first_name, :last_name, :email, presence: true + validates :email, uniqueness: true + + accepts_nested_attributes_for :profile + + def generate_token + begin + self[:auth_token] = SecureRandom.urlsafe_base64 + end while User.exists?(:auth_token => self[:auth_token]) + end + + def regenerate_auth_token + self.auth_token = nil + generate_token + save! + end + + def all_posts + self.posts.all.order('created_at DESC') + end + + def self.search(name) + name.split(" ").map do |n| + @users = [User.where('first_name ILIKE ?', "#{n}%")] + @users << User.where('last_name ILIKE ?', "#{n}%") + end.flatten.uniq + end + + def welcome_email + UserMailer.welcome(self).deliver! + end + + def name + self.first_name + " " + self.last_name + end + +end diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb new file mode 100644 index 000000000..918247075 --- /dev/null +++ b/app/views/comments/_comment.html.erb @@ -0,0 +1,22 @@ +
data-post-id=<%=comment.commentable.id %> > + +
\ No newline at end of file diff --git a/app/views/comments/_comment_comments.html.erb b/app/views/comments/_comment_comments.html.erb new file mode 100644 index 000000000..6db717eac --- /dev/null +++ b/app/views/comments/_comment_comments.html.erb @@ -0,0 +1,15 @@ +<% comment.comments.each do |c| %> +
+
+
+ <%= image_tag("default_image.jpg", class: "comment-picture") %> +
+
+

Commented by: <%= link_to comment.commentable.user.first_name + " " + comment.commentable.user.last_name, user_profiles_path(comment.commentable.user) %>

+

<%=comment.likes.count %> likes

+

<%= comment.content %>

+
+
+
+ +<% end %> \ No newline at end of file diff --git a/app/views/comments/_comment_new.html.erb b/app/views/comments/_comment_new.html.erb new file mode 100644 index 000000000..918247075 --- /dev/null +++ b/app/views/comments/_comment_new.html.erb @@ -0,0 +1,22 @@ +
data-post-id=<%=comment.commentable.id %> > + +
\ No newline at end of file diff --git a/app/views/comments/_new_comment.html.erb b/app/views/comments/_new_comment.html.erb new file mode 100644 index 000000000..7eac0975f --- /dev/null +++ b/app/views/comments/_new_comment.html.erb @@ -0,0 +1,10 @@ +
+ <%= image_tag("default_image.jpg", class: "comment-picture") %> +
+
+ <%= form_for [comment, new_comment] do |c| %> + <%= c.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= c.submit "Comment on Post", class: "btn btn-success" %> + <% end %> +
+ diff --git a/app/views/comments/create.js.erb b/app/views/comments/create.js.erb new file mode 100644 index 000000000..7c64b48f5 --- /dev/null +++ b/app/views/comments/create.js.erb @@ -0,0 +1,7 @@ +var findLastComment = function () { + var postId = '<%= j(@comment.commentable.id.inspect) %>' + var $lastComment = $(".comment-show[data-post-id=" + postId + "]").last() + $lastComment.after("<%= j(render @comment, user: @user) %>") +} + +findLastComment(); diff --git a/app/views/comments/destroy.js.erb b/app/views/comments/destroy.js.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 000000000..f52b67a7c --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,18 @@ + + + + ProjectDanebook + <%= favicon_link_tag "favicon.ico" %> + <%= csrf_meta_tags %> + + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application'%> + + + + + <%= yield %> + + + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 000000000..cbd34d2e9 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 000000000..37f0bddbd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/app/views/photos/_no_photos.html.erb b/app/views/photos/_no_photos.html.erb new file mode 100644 index 000000000..5500dd7fc --- /dev/null +++ b/app/views/photos/_no_photos.html.erb @@ -0,0 +1,5 @@ +
+

+

There currently no photos!

+
+
\ No newline at end of file diff --git a/app/views/photos/index.html.erb b/app/views/photos/index.html.erb new file mode 100644 index 000000000..63d6778cc --- /dev/null +++ b/app/views/photos/index.html.erb @@ -0,0 +1,20 @@ +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> +<%= render "no_photos" if @photos.empty? %> +<%= render "shared/flash" %> +
+ <% if @user == current_user %> + <%= form_for [@user, Photo.new] do |f| %> + <%= f.file_field :picture, class: "btn center-block" %> + <%= f.submit :Upload!, class: "btn btn-primary center-block" %> + <% end %> + <% end %> +
+
+ <% @photos.each do |photo| %> +
+ <%= link_to image_tag(photo.picture.url), user_photo_path(photo.user, photo) %> +
+ <% end %> +
+ diff --git a/app/views/photos/show.html.erb b/app/views/photos/show.html.erb new file mode 100644 index 000000000..3cc2fd87d --- /dev/null +++ b/app/views/photos/show.html.erb @@ -0,0 +1,42 @@ +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> +<%= render "shared/flash" %> +


+
+
+ <%= link_to image_tag(@photo.picture.url), user_photo_path(@photo.user, @photo) %> +

Likes: <%= @photo.likes.count %>

+ <%= form_for [@photo, Comment.new] do |c| %> + <%= c.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= c.submit "Comment on Post", class: "btn btn-success comment-section" %> + <% end %> + <%= form_for [@photo, Like.new] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary like-section"%> + <% end %> +
+ +
+ <% if @photo.comments.count != 0 %> + <% @photo.comments.each do |comment| %> +
+
    +
  • +
    + <%= image_tag("default_image.jpg", class: "comment-picture") %> +
    +
    +

    Commented by: <%= link_to comment.commentable.user.first_name + " " + comment.commentable.user.last_name, user_profiles_path(comment.commentable.user) %>

    +

    <%=comment.likes.count %> likes

    +

    <%= comment.content %>

    +
    + <%= render "comments/comment_comments", comment: comment %> + <%= render "comments/new_comment",comment: comment, new_comment: Comment.new, like: Like.new %> +
    +
    +
  • +
+
+ <% end %> + <% end %> +
\ No newline at end of file diff --git a/app/views/posts/_new_post.html.erb b/app/views/posts/_new_post.html.erb new file mode 100644 index 000000000..17495ab43 --- /dev/null +++ b/app/views/posts/_new_post.html.erb @@ -0,0 +1,12 @@ +
+
+
Post
+
+ <%= form_for [user, post], remote: true do |p| %> + <%= p.text_area :content, placeholder: "What's on your mind?", class: "new-post-text-area", cols: 62, rows: 7 %>
+ <%= p.submit "Post to the world!", class: "btn btn-primary center-block" %> + <% end %> +
+
+
+
diff --git a/app/views/posts/_post.html.erb b/app/views/posts/_post.html.erb new file mode 100644 index 000000000..6a2d50c80 --- /dev/null +++ b/app/views/posts/_post.html.erb @@ -0,0 +1,38 @@ +
> +
+
+
+
+ <%= image_tag("default_image.jpg", class: "post-photo") %> +
+
+

Posted by: <%=link_to from_user(post), user_path(post.from)%> + on<%= post.created_at.strftime(' %b %d at %r')%>

+
+

<%= post.content %>

+

<%= post.likes.count %> likes

+ <% if !post.comments.empty? %> + <% post.all_comments.each do |comment| %> + <%= render partial: "comments/comment_new", locals: {comment: comment, user: user} %> + <% end %> + <% else %> + <% end %> +
+ <% if post %> + <%= form_for [post, post.comments.build], remote: true do |c| %> + <%= image_tag("default_image.jpg", class: "comment-picture") %> + <%= c.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= c.submit "Comment on Post", data: {post_id: post.id}, class: "btn btn-success comment-section" %> + <% end %> + <%= form_for [post, post.likes.build] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary like-section"%> + <% end %> + + <%= link_to "Delete Post", user_post_path(current_user, post), method: :delete, class: "btn btn-danger delete-section", remote: true, data: {post_id: post.id} if current_user == user%> + <% end %> +
+
+
+
+
diff --git a/app/views/posts/create.js.erb b/app/views/posts/create.js.erb new file mode 100644 index 000000000..07e5f3fa2 --- /dev/null +++ b/app/views/posts/create.js.erb @@ -0,0 +1 @@ +$(".row.append").after("<%= j(render @post, user: @user ) %>") \ No newline at end of file diff --git a/app/views/posts/destroy.js.erb b/app/views/posts/destroy.js.erb new file mode 100644 index 000000000..f2a93241b --- /dev/null +++ b/app/views/posts/destroy.js.erb @@ -0,0 +1,2 @@ +//Posts.listenToDelete(); + diff --git a/app/views/profiles/destroy.html.erb b/app/views/profiles/destroy.html.erb new file mode 100644 index 000000000..3f502cfd2 --- /dev/null +++ b/app/views/profiles/destroy.html.erb @@ -0,0 +1,2 @@ +

Profile#destroy

+

Find me in app/views/profile/destroy.html.erb

diff --git a/app/views/profiles/edit.html.erb b/app/views/profiles/edit.html.erb new file mode 100644 index 000000000..7c45d1707 --- /dev/null +++ b/app/views/profiles/edit.html.erb @@ -0,0 +1,74 @@ +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> +<%= render "shared/flash" %> + +
+
+
+ ABOUT + <%= link_to "Edit", edit_user_profiles_path(@user), class: "about-edit-link" %> +
+
+
+
+

Basic Information

+ <%= form_for @user do |p| %> +
+ Email: <%= p.text_field :email, placeholder: @user.email, class: "form-control" %> +
+
+ First Name: <%= p.text_field :first_name, placeholder: @user.first_name, class: "form-control" %> +
+
+ Last Name: <%= p.text_field :last_name, placeholder: @user.last_name, class: "form-control" %> +
+ <%= p.fields_for :profile do |user_profile| %> +
+ College: <%= user_profile.text_field :college, placeholder: @profile.college, class: "form-control" %> +
+
+ Hometown: <%= user_profile.text_field :hometown, placeholder: @profile.college, class: "form-control" %> +
+
+ Current Location: <%= user_profile.text_field :current_location, placeholder: @profile.college, class: "form-control" %> +
+
+ Telephone: <%= user_profile.text_field :telephone, placeholder: @profile.college, class: "form-control" %>
+
+
Birthday:
+
+ + <% + date_wrap_open = '
'.html_safe + date_wrap_close = '
'.html_safe + date_separator = "#{date_wrap_close}#{date_wrap_open}" + %> + <%= date_wrap_open %> + + <%= user_profile.date_select( + :birthday, + { + :order => [:month, :day, :year], + :date_separator => date_separator, + :start_year => Date.today.year, + :end_year => 150.years.ago.year + }, + {:class => 'form-control'} + ) %> + <%= date_wrap_close %> +
+

+
+

Words to Live by

+

<%= user_profile.text_area :about_me, placeholder: @profile.about_me, class: "form-control text-area-about", cols: 11, rows: 11 %>

+

About me

+

<%= user_profile.text_area :words_to_live_by, placeholder: @profile.words_to_live_by, class: "form-control text-area-about", cols: 11, rows: 11%>

+
+ <%= p.submit :Submit, class: "form-control btn btn-primary submit-edit-button" %> +
+ <% end %> +
+ <% end %> +
+
+ \ No newline at end of file diff --git a/app/views/profiles/show.html.erb b/app/views/profiles/show.html.erb new file mode 100644 index 000000000..cfe675f87 --- /dev/null +++ b/app/views/profiles/show.html.erb @@ -0,0 +1,29 @@ +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> +<%= render "shared/flash" %> +<%= render "relationships/pending_friend_request" %> + +
+
+
+ ABOUT + <%= link_to "Edit", edit_user_profiles_path(@user), class: "about-edit-link" if current_user == @user%> +
+
+
+
+

Basic Information

+ Name: <%= @profile.user.first_name.capitalize %> <%= @profile.user.last_name.capitalize %>
+ College: <%= @profile.college %>
+ Current Location: <%= @profile.current_location %>
+ Hometown: <%= @profile.hometown %>
+ Telephone: <%= @profile.telephone %> +

+ +
+

Words to Live by

+

<%= @profile.about_me%>

+

About me

+

<%= @profile.words_to_live_by%>

+
+
\ No newline at end of file diff --git a/app/views/profiles/update.html.erb b/app/views/profiles/update.html.erb new file mode 100644 index 000000000..e3e90c203 --- /dev/null +++ b/app/views/profiles/update.html.erb @@ -0,0 +1,2 @@ +

Profile#update

+

Find me in app/views/profile/update.html.erb

diff --git a/app/views/relationships/_pending_friend_request.html.erb b/app/views/relationships/_pending_friend_request.html.erb new file mode 100644 index 000000000..3caf99ebb --- /dev/null +++ b/app/views/relationships/_pending_friend_request.html.erb @@ -0,0 +1,14 @@ +
+ <% current_user.been_friended.each do |friend_request| %> + <% user = User.find(friend_request.id) %> + <% if !current_user.friends.include?(user) %> + <%= user.name %> wants to be you friend! + <%= form_for(current_user.friended_relationships.build) do |f| %> +
<%= hidden_field_tag :friended_id, user.id %>
+ <%= f.submit "Friend", class: "btn btn-primary same-line" %> + <% end %> + <%= link_to "Delete request", relationship_path(Relationship.where(friender_id: user.id ).where(friended_id: current_user.id).first), + method: :delete, data: {confirm: "Are you sure you want to delete #{user.name}'s friend request?"}, class: "btn btn-danger same-line" %> + <% end %> + <% end %> +
diff --git a/app/views/relationships/index.html.erb b/app/views/relationships/index.html.erb new file mode 100644 index 000000000..0e921c27a --- /dev/null +++ b/app/views/relationships/index.html.erb @@ -0,0 +1,12 @@ +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> +<%= render "shared/flash" %> +
+ <% @friends.each do |friend| %> +
+ <% if (@user == current_user) || (friend.friends.include?(current_user)) %> + <%= link_to friend.name, user_profiles_path(friend) %> + <% end %> +
+ <% end %> +
\ No newline at end of file diff --git a/app/views/shared/_flash.html.erb b/app/views/shared/_flash.html.erb new file mode 100644 index 000000000..f2c7dbfb6 --- /dev/null +++ b/app/views/shared/_flash.html.erb @@ -0,0 +1,5 @@ +
+ <% flash.each do |key, value| %> +
<%= value %>
+ <% end %> +
\ No newline at end of file diff --git a/app/views/shared/_friend_button.html.erb b/app/views/shared/_friend_button.html.erb new file mode 100644 index 000000000..29edd5e6d --- /dev/null +++ b/app/views/shared/_friend_button.html.erb @@ -0,0 +1,21 @@ +<% if current_user %> + <% if current_user == @user %> + <%= link_to "Edit", edit_user_profiles_path(@user) %> + <% elsif !current_user.friends.include?(@user) %> + <%= form_for(current_user.friended_relationships.build) do |f| %> +
<%= hidden_field_tag :friended_id, @user.id %>
+ <%= f.submit "Friend", class: "btn btn-primary" %> + <% end %> + <% elsif current_user.friends.include?(@user) && !@user.friends.include?(current_user) %> + <%= form_for(current_user.friended_relationships.find_by(friended_id: @user.id), + html: { method: :delete }, + data: {confirm: "Are you sure you want to delete that friend request?"}) do |f| %> + <%= f.submit "Pending Friend Request", class: "btn btn-danger" %> + <% end %> + <% else %> + <%= form_for(current_user.friended_relationships.find_by(friended_id: @user.id), + html: { method: :delete }) do |f| %> + <%= f.submit "Unfriend", class: "btn btn-danger" %> + <% end %> + <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/shared/_profile_cover_photos.html.erb b/app/views/shared/_profile_cover_photos.html.erb new file mode 100644 index 000000000..783bc3375 --- /dev/null +++ b/app/views/shared/_profile_cover_photos.html.erb @@ -0,0 +1,2 @@ +<%= image_tag("default_image.jpg", class: "profile-photo") %> +<%= image_tag("default_cover.jpg", class: "cover-photo") %> \ No newline at end of file diff --git a/app/views/shared/_profile_header.html.erb b/app/views/shared/_profile_header.html.erb new file mode 100644 index 000000000..96dfd2754 --- /dev/null +++ b/app/views/shared/_profile_header.html.erb @@ -0,0 +1,24 @@ +
+
+
+ <%= render "shared/profile_cover_photos" %> +
+
+
+ + + + + +
+
\ No newline at end of file diff --git a/app/views/shared/_search_navbar.html.erb b/app/views/shared/_search_navbar.html.erb new file mode 100644 index 000000000..93f21716d --- /dev/null +++ b/app/views/shared/_search_navbar.html.erb @@ -0,0 +1,47 @@ +
+ + diff --git a/app/views/user_mailer/commented_by.html.erb b/app/views/user_mailer/commented_by.html.erb new file mode 100644 index 000000000..4cd109e72 --- /dev/null +++ b/app/views/user_mailer/commented_by.html.erb @@ -0,0 +1,5 @@ +

Hi <%= link_to @user.first_name, @user %>!

+ +

<%= link_to @commenter.first_name, @commenter %> just commented on a <%= @object.class.to_s.downcase%> of yours!

+ +

You can see it <%= link_to "here", "http://localhost:3000/users/#{@user.id}" %>

diff --git a/app/views/user_mailer/commented_by.text.erb b/app/views/user_mailer/commented_by.text.erb new file mode 100644 index 000000000..738d46296 --- /dev/null +++ b/app/views/user_mailer/commented_by.text.erb @@ -0,0 +1,5 @@ +Hi <%= @user.first_name %>! + +<%= @commenter.first_name %> just commented on a <%= @object.class.to_s.downcase%> of yours! + +You can see it here: <%= link_to @user.first_name, "http://localhost:3000/users/#{@user.id}" %> diff --git a/app/views/user_mailer/welcome.html.erb b/app/views/user_mailer/welcome.html.erb new file mode 100644 index 000000000..2f1e82ae5 --- /dev/null +++ b/app/views/user_mailer/welcome.html.erb @@ -0,0 +1,9 @@ +

Welcome!

+ +

+ Hi <%= @user.first_name %>, welcome to DaneBook!! +

+ +

+ View your account at <%= link_to @user.first_name, "http://localhost:3000/users/#{@user.id}" %> +

\ No newline at end of file diff --git a/app/views/user_mailer/welcome.text.erb b/app/views/user_mailer/welcome.text.erb new file mode 100644 index 000000000..19081ea6b --- /dev/null +++ b/app/views/user_mailer/welcome.text.erb @@ -0,0 +1,3 @@ +Hi <%= @user.first_name %>, and welcome to Danebook! + +View your account at <%= link_to @user.first_name, "http://localhost:3000/users/#{@user.id}" %> \ No newline at end of file diff --git a/app/views/users/_new_navbar.html.erb b/app/views/users/_new_navbar.html.erb new file mode 100644 index 000000000..a6c98dfc3 --- /dev/null +++ b/app/views/users/_new_navbar.html.erb @@ -0,0 +1,47 @@ + + diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 000000000..1881fbdba --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,2 @@ +

Users#edit

+

Find me in app/views/users/edit.html.erb

diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb new file mode 100644 index 000000000..f2381a2fb --- /dev/null +++ b/app/views/users/index.html.erb @@ -0,0 +1,9 @@ +

Users#index

+

Find me in app/views/users/index.html.erb

+<%= render "shared/flash" %> + +<% @users.each do |user| %> + <%= link_to user.first_name + + user.last_name, user_path(user) %>
+ <%= user.email %>

+<% end %> \ No newline at end of file diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 000000000..d1d0cb96a --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,38 @@ +<%= render "new_navbar" %> +<%= render "shared/flash" %> + +
+
+
+

Connect with all of your friends!

+
    +
  • See photos and updates in your news feed
  • +
  • Post your status for the world to see on your proile
  • +
  • Get in your with your friends by "friending" them
  • +
  • Like things beacause you are a postive person!
  • +
+
+
+ <%= form_for @user do |f| %> + + + + + +
+ <%= f.submit "Sign Up!", class: "btn btn-success sign-up-field" %> +
+ <% end %> +
+
+
\ No newline at end of file diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb new file mode 100644 index 000000000..39b613b4b --- /dev/null +++ b/app/views/users/show.html.erb @@ -0,0 +1,38 @@ +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> +<%= render "shared/flash" %> +<%= render "relationships/pending_friend_request" %> +
+
+
+
+

About

+

Name: <%= @user.first_name.capitalize + " " + @user.last_name.capitalize %>


+

College: <%= @profile.college %>

+

Current Location: <%= @profile.current_location %>

+

Hometown: <%= @profile.hometown %>

+

+
+
+

Photos

<%= @user.photos.count %>

+ <% @photos.each do |photo| %> +
+ <%= link_to image_tag(photo.picture.url, class: "resize-index-photo"), user_photo_path(photo.user, photo) %> +
+ <% end %> +
+
+

Friends

+
+
+ <% if current_user #&& current_user.friends.include?(@user) %> +
+
+ <%= render partial: "posts/new_post", locals: {post: @post, user: @user} if @post && current_user%> + <% @posts.each do |post|%> + <%= render partial: "posts/post", locals: {post: post, user: @user} %> + <% end %> +
+
+ <% end %> +
diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 000000000..66e9889e8 --- /dev/null +++ b/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails new file mode 100755 index 000000000..5badb2fde --- /dev/null +++ b/bin/rails @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake new file mode 100755 index 000000000..d87d5f578 --- /dev/null +++ b/bin/rake @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 000000000..e620b4dad --- /dev/null +++ b/bin/setup @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/spring b/bin/spring new file mode 100755 index 000000000..7fe232c3a --- /dev/null +++ b/bin/spring @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. + +unless defined?(Spring) + require 'rubygems' + require 'bundler' + + if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) + Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } + gem 'spring', match[1] + require 'spring/binstub' + end +end diff --git a/bin/update b/bin/update new file mode 100755 index 000000000..a8e4462f2 --- /dev/null +++ b/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/config.ru b/config.ru new file mode 100644 index 000000000..f7ba0b527 --- /dev/null +++ b/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 000000000..af54ad3ac --- /dev/null +++ b/config/application.rb @@ -0,0 +1,15 @@ +require_relative 'boot' + +require 'rails/all' + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module ProjectDanebook + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 000000000..30f5120df --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 000000000..0bbde6f74 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,9 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 000000000..57fcbb54e --- /dev/null +++ b/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: postgresql + pool: 5 + timeout: 5000 + +development: + <<: *default + database: db/danebook_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/danebook_test + +production: + <<: *default + database: db/danebook_production diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 000000000..426333bb4 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 000000000..ae2427fca --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,75 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + Paperclip.options[:command_path] = "/usr/local/bin" + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + config.action_mailer.delivery_method = :letter_opener + + config.action_mailer.default_url_options = {:host => 'localhost:3000'} + + config.paperclip_defaults = { + + :storage => :s3, + + :s3_credentials => { + + + :host_name => "dylanlynch.s3-website-us-east-1.amazonaws.com", + + + :bucket => Rails.application.secrets.s3_bucket_name, + :access_key_id => Rails.application.secrets.s3_api_id, + :secret_access_key => Rails.application.secrets.s3_api_key, + :s3_region=> Rails.application.secrets.s3_region + } +} + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. + config.assets.debug = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 000000000..18833845a --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,108 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + config.action_mailer.default_url_options = { + :host => 'https://stormy-forest-22873.herokuapp.com/', + } + + config.action_mailer.delivery_method = :letter_opener + + config.paperclip_defaults = { + + :storage => :s3, + + :s3_credentials => { + + + :host_name => "dylanlynch.s3-website-us-east-1.amazonaws.com", + + + :bucket => Rails.application.secrets.s3_bucket_name, + :access_key_id => Rails.application.secrets.s3_api_id, + :secret_access_key => Rails.application.secrets.s3_api_key, + :s3_region=> Rails.application.secrets.s3_region + } +} + # Compress JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "project_danebook_#{Rails.env}" + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 000000000..30587ef6d --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,42 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=3600' + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +end diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 000000000..51639b67a --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 000000000..01ef3e663 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,11 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. +# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 000000000..59385cdf3 --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 000000000..5a6a32d37 --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 000000000..4a994e1e7 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 000000000..ac033bf9d --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 000000000..dc1899682 --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb new file mode 100644 index 000000000..0706cafd4 --- /dev/null +++ b/config/initializers/new_framework_defaults.rb @@ -0,0 +1,24 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.0 upgrade. +# +# Read the Rails 5.0 release notes for more info on each option. + +# Enable per-form CSRF tokens. Previous versions had false. +Rails.application.config.action_controller.per_form_csrf_tokens = true + +# Enable origin-checking CSRF mitigation. Previous versions had false. +Rails.application.config.action_controller.forgery_protection_origin_check = true + +# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. +# Previous versions had false. +ActiveSupport.to_time_preserves_timezone = true + +# Require `belongs_to` associations by default. Previous versions had false. +Rails.application.config.active_record.belongs_to_required_by_default = true + +# Do not halt callback chains when a callback returns false. Previous versions had true. +ActiveSupport.halt_callback_chains_on_return_false = false + +# Configure SSL options to enable HSTS with subdomains. Previous versions had false. +Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb new file mode 100644 index 000000000..f2d118131 --- /dev/null +++ b/config/initializers/session_store.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.session_store :cookie_store, key: '_project_danebook_session' diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 000000000..bbfc3961b --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 000000000..065395716 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,23 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 000000000..c7f311f81 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,47 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum, this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted this block will be run, if you are using `preload_app!` +# option you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 000000000..7733f6824 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,38 @@ +Rails.application.routes.draw do + + get 'comments/new' + + resources :users do + resource :profiles, only: [:edit, :show, :update] + resources :posts, only: [:create, :destroy] + resources :photos + resources :relationships, only: [:index] + end + + resources :posts do + resources :comments, defaults: {commentable: "Post"} + resources :likes, defaults: {likeable: "Post"} + end + + resources :comments do + resources :comments, defaults: {commentable: "Comment"} + resources :likes, defaults: {likeable: "Comment"} + end + + resources :photos do + resources :comments, defaults: {commentable: "Photo"} + resources :likes, defaults: {likeable: "Photo"} + end + + resources :relationships, only: [:create, :destroy] + + root "users#new" + + resource :session, :only => [:new, :create, :destroy] + get "login" => "sessions#new" + delete "logout" => "sessions#destroy" + + get '*path' => redirect("/") + + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 000000000..507796a0a --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,30 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: 4a8ab98976c8adefce7471fd7a3d1c240af246cc1dc57ead037ac053faab4f42b5d9f0ee2c3017d9825c535efe6a3e6e87ca18b841207161b336a9e14f5cfee4 + s3_bucket_name: <%= ENV["S3_BUCKET_NAME"] %> + s3_api_id: <%= ENV["S3_API_ID"] %> + s3_api_key: <%= ENV["S3_API_KEY"] %> + s3_region: <%= ENV["S3_REGION"] %> + +test: + secret_key_base: 8ed5c1fef5f8594431a217909d96653bfd594285b6180e7786c16347f7f4671ae5a32a2104643aeaf846a87f832b0f59cbc2595fe2c204f720ae792e1a23b17c + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> + s3_bucket_name: <%= ENV["S3_BUCKET_NAME"] %> + s3_api_id: <%= ENV["S3_API_ID"] %> + s3_api_key: <%= ENV["S3_API_KEY"] %> + s3_region: <%= ENV["S3_REGION"] %> diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 000000000..c9119b40c --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/db/migrate/20160810213213_create_users.rb b/db/migrate/20160810213213_create_users.rb new file mode 100644 index 000000000..851ed6b47 --- /dev/null +++ b/db/migrate/20160810213213_create_users.rb @@ -0,0 +1,8 @@ +class CreateUsers < ActiveRecord::Migration[5.0] + def change + create_table :users do |t| + + t.timestamps + end + end +end diff --git a/db/migrate/20160810214240_add_password_and_email_to_user.rb b/db/migrate/20160810214240_add_password_and_email_to_user.rb new file mode 100644 index 000000000..2024bb0fd --- /dev/null +++ b/db/migrate/20160810214240_add_password_and_email_to_user.rb @@ -0,0 +1,6 @@ +class AddPasswordAndEmailToUser < ActiveRecord::Migration[5.0] + def change + add_column :users, :email, :string + add_column :users, :password_digest, :string + end +end diff --git a/db/migrate/20160810220240_add_auth_token.rb b/db/migrate/20160810220240_add_auth_token.rb new file mode 100644 index 000000000..c0f80d652 --- /dev/null +++ b/db/migrate/20160810220240_add_auth_token.rb @@ -0,0 +1,6 @@ +class AddAuthToken < ActiveRecord::Migration[5.0] + def change + add_column :users, :auth_token, :string + add_index :users, :auth_token, :unique => true + end +end diff --git a/db/migrate/20160810232400_create_profiles.rb b/db/migrate/20160810232400_create_profiles.rb new file mode 100644 index 000000000..b700877f2 --- /dev/null +++ b/db/migrate/20160810232400_create_profiles.rb @@ -0,0 +1,15 @@ +class CreateProfiles < ActiveRecord::Migration[5.0] + def change + create_table :profiles do |t| + t.references :user, foreign_key: true + t.string :month + t.integer :year + t.integer :day + t.string :college + t.string :hometown + t.string :current_location + t.string :telephone + t.timestamps + end + end +end diff --git a/db/migrate/20160811195710_add_first_name_last_name_to_user.rb b/db/migrate/20160811195710_add_first_name_last_name_to_user.rb new file mode 100644 index 000000000..f71848b74 --- /dev/null +++ b/db/migrate/20160811195710_add_first_name_last_name_to_user.rb @@ -0,0 +1,6 @@ +class AddFirstNameLastNameToUser < ActiveRecord::Migration[5.0] + def change + add_column :users, :first_name, :string + add_column :users, :last_name, :string + end +end diff --git a/db/migrate/20160811214951_create_photos.rb b/db/migrate/20160811214951_create_photos.rb new file mode 100644 index 000000000..5a536ef74 --- /dev/null +++ b/db/migrate/20160811214951_create_photos.rb @@ -0,0 +1,9 @@ +class CreatePhotos < ActiveRecord::Migration[5.0] + def change + create_table :photos do |t| + t.references :profile, foreign_key: true + t.string :url + t.timestamps + end + end +end diff --git a/db/migrate/20160812160556_add_profile_text_attributes.rb b/db/migrate/20160812160556_add_profile_text_attributes.rb new file mode 100644 index 000000000..663201851 --- /dev/null +++ b/db/migrate/20160812160556_add_profile_text_attributes.rb @@ -0,0 +1,6 @@ +class AddProfileTextAttributes < ActiveRecord::Migration[5.0] + def change + add_column :profiles, :about_me, :text + add_column :profiles, :words_to_live_by, :text + end +end diff --git a/db/migrate/20160812180656_add_birthday_to_profile.rb b/db/migrate/20160812180656_add_birthday_to_profile.rb new file mode 100644 index 000000000..cf9391c24 --- /dev/null +++ b/db/migrate/20160812180656_add_birthday_to_profile.rb @@ -0,0 +1,5 @@ +class AddBirthdayToProfile < ActiveRecord::Migration[5.0] + def change + add_column :profiles, :birthday, :date + end +end diff --git a/db/migrate/20160812182041_create_posts.rb b/db/migrate/20160812182041_create_posts.rb new file mode 100644 index 000000000..c2daa0c3e --- /dev/null +++ b/db/migrate/20160812182041_create_posts.rb @@ -0,0 +1,10 @@ +class CreatePosts < ActiveRecord::Migration[5.0] + def change + create_table :posts do |t| + t.references :user, foreign_key: true + t.text :content + + t.timestamps + end + end +end diff --git a/db/migrate/20160812221758_create_comments.rb b/db/migrate/20160812221758_create_comments.rb new file mode 100644 index 000000000..b4c493fb1 --- /dev/null +++ b/db/migrate/20160812221758_create_comments.rb @@ -0,0 +1,11 @@ +class CreateComments < ActiveRecord::Migration[5.0] + def change + create_table :comments do |t| + t.references :user + t.string :type + t.text :content + + t.timestamps + end + end +end diff --git a/db/migrate/20160812225137_add_unique_to_email.rb b/db/migrate/20160812225137_add_unique_to_email.rb new file mode 100644 index 000000000..e1bbce28f --- /dev/null +++ b/db/migrate/20160812225137_add_unique_to_email.rb @@ -0,0 +1,5 @@ +class AddUniqueToEmail < ActiveRecord::Migration[5.0] + def change + add_index :users, :email, :unique => true + end +end diff --git a/db/migrate/20160814021709_add_to_id_to_posts.rb b/db/migrate/20160814021709_add_to_id_to_posts.rb new file mode 100644 index 000000000..94466231b --- /dev/null +++ b/db/migrate/20160814021709_add_to_id_to_posts.rb @@ -0,0 +1,5 @@ +class AddToIdToPosts < ActiveRecord::Migration[5.0] + def change + add_column :posts, :from, :integer + end +end diff --git a/db/migrate/20160814151629_add_commentable_type_id.rb b/db/migrate/20160814151629_add_commentable_type_id.rb new file mode 100644 index 000000000..ff1b2b2b8 --- /dev/null +++ b/db/migrate/20160814151629_add_commentable_type_id.rb @@ -0,0 +1,7 @@ +class AddCommentableTypeId < ActiveRecord::Migration[5.0] + def change + add_column :comments, :commentable_type, :string + add_column :comments, :commentable_id, :integer + add_index :comments, [:commentable_id, :commentable_type] + end +end diff --git a/db/migrate/20160815020752_remove_user_id_from_comment.rb b/db/migrate/20160815020752_remove_user_id_from_comment.rb new file mode 100644 index 000000000..0db3513ff --- /dev/null +++ b/db/migrate/20160815020752_remove_user_id_from_comment.rb @@ -0,0 +1,5 @@ +class RemoveUserIdFromComment < ActiveRecord::Migration[5.0] + def change + remove_column :comments, :user_id + end +end diff --git a/db/migrate/20160815030937_create_likes.rb b/db/migrate/20160815030937_create_likes.rb new file mode 100644 index 000000000..fb550bdb0 --- /dev/null +++ b/db/migrate/20160815030937_create_likes.rb @@ -0,0 +1,11 @@ +class CreateLikes < ActiveRecord::Migration[5.0] + def change + create_table :likes do |t| + t.string :likeable_type + t.integer :likeable_id + t.references :user + t.timestamps + end + add_index :likes, [:likeable_id, :likeable_type] + end +end diff --git a/db/migrate/20160818214302_add_avatar_to_photos.rb b/db/migrate/20160818214302_add_avatar_to_photos.rb new file mode 100644 index 000000000..6bfc1a25a --- /dev/null +++ b/db/migrate/20160818214302_add_avatar_to_photos.rb @@ -0,0 +1,7 @@ +class AddAvatarToPhotos < ActiveRecord::Migration[5.0] + def change + add_attachment :photos, :picture + add_column :profiles, :cover_photo_id, :integer + add_column :profiles, :profile_photo_id, :integer + end +end diff --git a/db/migrate/20160819192645_add_from_on_comment_table.rb b/db/migrate/20160819192645_add_from_on_comment_table.rb new file mode 100644 index 000000000..be3b71462 --- /dev/null +++ b/db/migrate/20160819192645_add_from_on_comment_table.rb @@ -0,0 +1,11 @@ +class AddFromOnCommentTable < ActiveRecord::Migration[5.0] + def change + add_column :comments, :from, :integer + add_column :likes, :from, :integer + add_column :comments, :user_id, :integer + add_index :comments, :user_id + add_column :photos, :user_id, :integer + add_index :photos, :user_id + remove_column :photos, :profile_id + end +end diff --git a/db/migrate/20160822172215_create_relationships.rb b/db/migrate/20160822172215_create_relationships.rb new file mode 100644 index 000000000..308c577b3 --- /dev/null +++ b/db/migrate/20160822172215_create_relationships.rb @@ -0,0 +1,12 @@ +class CreateRelationships < ActiveRecord::Migration[5.0] + def change + create_table :relationships do |t| + t.integer :friender_id + t.integer :friended_id + t.timestamps + end + add_index :relationships, :friender_id + add_index :relationships, :friended_id + add_index :relationships, [:friender_id, :friended_id], unique: true + end +end diff --git a/db/migrate/20160822173552_add_description_to_pictures.rb b/db/migrate/20160822173552_add_description_to_pictures.rb new file mode 100644 index 000000000..01f4e2ebc --- /dev/null +++ b/db/migrate/20160822173552_add_description_to_pictures.rb @@ -0,0 +1,5 @@ +class AddDescriptionToPictures < ActiveRecord::Migration[5.0] + def change + add_column :photos, :description, :string + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..78e5dfdcd --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,107 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20160822173552) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "comments", force: :cascade do |t| + t.string "type" + t.text "content" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "commentable_type" + t.integer "commentable_id" + t.integer "from" + t.integer "user_id" + t.index ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type", using: :btree + t.index ["user_id"], name: "index_comments_on_user_id", using: :btree + end + + create_table "likes", force: :cascade do |t| + t.string "likeable_type" + t.integer "likeable_id" + t.integer "user_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "from" + t.index ["likeable_id", "likeable_type"], name: "index_likes_on_likeable_id_and_likeable_type", using: :btree + t.index ["user_id"], name: "index_likes_on_user_id", using: :btree + end + + create_table "photos", force: :cascade do |t| + t.string "url" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "picture_file_name" + t.string "picture_content_type" + t.integer "picture_file_size" + t.datetime "picture_updated_at" + t.integer "user_id" + t.string "description" + t.index ["user_id"], name: "index_photos_on_user_id", using: :btree + end + + create_table "posts", force: :cascade do |t| + t.integer "user_id" + t.text "content" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "from" + t.index ["user_id"], name: "index_posts_on_user_id", using: :btree + end + + create_table "profiles", force: :cascade do |t| + t.integer "user_id" + t.string "month" + t.integer "year" + t.integer "day" + t.string "college" + t.string "hometown" + t.string "current_location" + t.string "telephone" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "about_me" + t.text "words_to_live_by" + t.date "birthday" + t.integer "cover_photo_id" + t.integer "profile_photo_id" + t.index ["user_id"], name: "index_profiles_on_user_id", using: :btree + end + + create_table "relationships", force: :cascade do |t| + t.integer "friender_id" + t.integer "friended_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["friended_id"], name: "index_relationships_on_friended_id", using: :btree + t.index ["friender_id", "friended_id"], name: "index_relationships_on_friender_id_and_friended_id", unique: true, using: :btree + t.index ["friender_id"], name: "index_relationships_on_friender_id", using: :btree + end + + create_table "users", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "email" + t.string "password_digest" + t.string "auth_token" + t.string "first_name" + t.string "last_name" + t.index ["auth_token"], name: "index_users_on_auth_token", unique: true, using: :btree + t.index ["email"], name: "index_users_on_email", unique: true, using: :btree + end + + add_foreign_key "posts", "users" + add_foreign_key "profiles", "users" +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 000000000..1beea2acc --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). +# +# Examples: +# +# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) +# Character.create(name: 'Luke', movie: movies.first) diff --git a/lib/assets/.keep b/lib/assets/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/log/.keep b/log/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/public/404.html b/public/404.html new file mode 100644 index 000000000..b612547fc --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

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

+
+

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

+
+ + diff --git a/public/422.html b/public/422.html new file mode 100644 index 000000000..a21f82b3b --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

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

+
+

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

+
+ + diff --git a/public/500.html b/public/500.html new file mode 100644 index 000000000..061abc587 --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

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

+
+ + diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png new file mode 100644 index 000000000..e69de29bb diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 000000000..e69de29bb diff --git a/public/assets/.sprockets-manifest-b9948bb8566a35aec92fd667b17be5fd.json b/public/assets/.sprockets-manifest-b9948bb8566a35aec92fd667b17be5fd.json new file mode 100644 index 000000000..b58fd8ca8 --- /dev/null +++ b/public/assets/.sprockets-manifest-b9948bb8566a35aec92fd667b17be5fd.json @@ -0,0 +1 @@ +{"files":{"default_cover-d70aaff22ab469562904a3a3d8f3aeccc2bfcf332937e34790d5b83da9c957d3.jpg":{"logical_path":"default_cover.jpg","mtime":"2016-08-11T17:53:32-04:00","size":73659,"digest":"d70aaff22ab469562904a3a3d8f3aeccc2bfcf332937e34790d5b83da9c957d3","integrity":"sha256-1wqv8iq0aVYpBKOj2POuzMK/zzMpN+NHkNW4PanJV9M="},"default_image-0a53c845db5e15f0d7909403449f70530f27f26acc32f0d1c5dcb95c3c113822.jpg":{"logical_path":"default_image.jpg","mtime":"2016-08-11T17:53:03-04:00","size":12773,"digest":"0a53c845db5e15f0d7909403449f70530f27f26acc32f0d1c5dcb95c3c113822","integrity":"sha256-ClPIRdteFfDXkJQDRJ9wUw8n8mrMMvDRxdy5XDwROCI="},"favicon-22ff34c79cbd261c1159f0679bfb0aa57320c2b64d034f6343905c5b077728e4.ico":{"logical_path":"favicon.ico","mtime":"2016-08-19T14:42:02-04:00","size":1150,"digest":"22ff34c79cbd261c1159f0679bfb0aa57320c2b64d034f6343905c5b077728e4","integrity":"sha256-Iv80x5y9JhwRWfBnm/sKpXMgwrZNA09jQ5BcWwd3KOQ="},"application-5b998f4133e4f592baac2d30f655b330a487e1c442edc1bcb3468b67ee377c5f.js":{"logical_path":"application.js","mtime":"2016-08-22T18:33:36-04:00","size":117653,"digest":"5b998f4133e4f592baac2d30f655b330a487e1c442edc1bcb3468b67ee377c5f","integrity":"sha256-W5mPQTPk9ZK6rC0w9lWzMKSH4cRC7cG8s0aLZ+43fF8="},"application-ffb6220311116165a5af41cd467c7c3e8ef6ffb212fc966bc1037e35a9066d65.css":{"logical_path":"application.css","mtime":"2016-08-22T18:33:36-04:00","size":2930,"digest":"ffb6220311116165a5af41cd467c7c3e8ef6ffb212fc966bc1037e35a9066d65","integrity":"sha256-/7YiAxERYWWlr0HNRnx8Po72/7IS/JZrwQN+NakGbWU="}},"assets":{"default_cover.jpg":"default_cover-d70aaff22ab469562904a3a3d8f3aeccc2bfcf332937e34790d5b83da9c957d3.jpg","default_image.jpg":"default_image-0a53c845db5e15f0d7909403449f70530f27f26acc32f0d1c5dcb95c3c113822.jpg","favicon.ico":"favicon-22ff34c79cbd261c1159f0679bfb0aa57320c2b64d034f6343905c5b077728e4.ico","application.js":"application-5b998f4133e4f592baac2d30f655b330a487e1c442edc1bcb3468b67ee377c5f.js","application.css":"application-ffb6220311116165a5af41cd467c7c3e8ef6ffb212fc966bc1037e35a9066d65.css"}} \ No newline at end of file diff --git a/public/assets/application-5b998f4133e4f592baac2d30f655b330a487e1c442edc1bcb3468b67ee377c5f.js b/public/assets/application-5b998f4133e4f592baac2d30f655b330a487e1c442edc1bcb3468b67ee377c5f.js new file mode 100644 index 000000000..935bfdb25 --- /dev/null +++ b/public/assets/application-5b998f4133e4f592baac2d30f655b330a487e1c442edc1bcb3468b67ee377c5f.js @@ -0,0 +1,4 @@ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function i(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,i){return!!t.call(e,i,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Te.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function r(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(je)||[],function(e,n){t[n]=!0}),t}function a(){ie.addEventListener?(ie.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(ie.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(ie.addEventListener||"load"===e.event.type||"complete"===ie.readyState)&&(a(),pe.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var i="data-"+t.replace(Re,"-$1").toLowerCase();if(n=e.getAttribute(i),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(e){}pe.data(e,t,n)}else n=void 0}return n}function u(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,i){if(He(e)){var r,o,a=pe.expando,s=e.nodeType,l=s?pe.cache:e,u=s?e[a]:e[a]&&a;if(u&&l[u]&&(i||l[u].data)||void 0!==n||"string"!=typeof t)return u||(u=s?e[a]=ne.pop()||pe.guid++:a),l[u]||(l[u]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(i?l[u]=pe.extend(l[u],t):l[u].data=pe.extend(l[u].data,t)),o=l[u],i||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(r=o[t],null==r&&(r=o[pe.camelCase(t)])):r=o,r}}function d(e,t,n){if(He(e)){var i,r,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(i=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in i?t=[t]:(t=pe.camelCase(t),t=t in i?[t]:t.split(" ")),r=t.length;for(;r--;)delete i[t[r]];if(n?!u(i):!pe.isEmptyObject(i))return}(n||(delete a[s].data,u(a[s])))&&(o?pe.cleanData([e],!0):de.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function f(e,t,n,i){var r,o=1,a=20,s=i?function(){return i.cur()}:function(){return pe.css(e,t,"")},l=s(),u=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==u&&+l)&&Me.exec(pe.css(e,t));if(c&&c[3]!==u){u=u||c[3],n=n||[],c=+l||1;do o=o||".5",c/=o,pe.style(e,t,c+u);while(o!==(o=s()/l)&&1!==o&&--a)}return n&&(c=+c||+l||0,r=n[1]?c+(n[1]+1)*n[2]:+n[2],i&&(i.unit=u,i.start=c,i.end=r)),r}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,i,r=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(i=n[r]);r++)!t||pe.nodeName(i,t)?o.push(i):pe.merge(o,h(i,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function m(e,t){for(var n,i=0;null!=(n=e[i]);i++)pe._data(n,"globalEval",!t||pe._data(t[i],"globalEval"))}function g(e){Ie.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,i,r){for(var o,a,s,l,u,c,d,f=e.length,y=p(t),v=[],b=0;b"!==d[1]||Ve.test(a)?0:l:l.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,l.childNodes),l.textContent="";l.firstChild;)l.removeChild(l.firstChild);l=y.lastChild}else v.push(t.createTextNode(a));for(l&&y.removeChild(l),de.appendChecked||pe.grep(h(v,"input"),g),b=0;a=v[b++];)if(i&&pe.inArray(a,i)>-1)r&&r.push(a);else if(s=pe.contains(a.ownerDocument,a),l=h(y.appendChild(a),"script"),s&&m(l),n)for(o=0;a=l[o++];)Be.test(a.type||"")&&n.push(a);return l=null,y}function v(){return!0}function b(){return!1}function x(){try{return ie.activeElement}catch(e){}}function w(e,t,n,i,r,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(i=i||n,n=void 0);for(s in t)w(e,s,n,i,t[s],o);return e}if(null==i&&null==r?(r=n,i=n=void 0):null==r&&("string"==typeof n?(r=i,i=void 0):(r=i,i=n,n=void 0)),r===!1)r=b;else if(!r)return e;return 1===o&&(a=r,r=function(e){return pe().off(e),a.apply(this,arguments)},r.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,r,i,n)})}function C(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function T(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function S(e){var t=rt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function E(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,i,r,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(i=0,r=s[n].length;i1&&"string"==typeof p&&!de.checkClone&&it.test(p))return e.each(function(r){var o=e.eq(r);m&&(t[0]=p.call(this,r,o.html())),A(o,t,n,i)});if(d&&(u=y(t,e[0].ownerDocument,!1,e,i),r=u.firstChild,1===u.childNodes.length&&(u=r),r||i)){for(s=pe.map(h(u,"script"),T),a=s.length;c")).appendTo(t.documentElement),t=(lt[0].contentWindow||lt[0].contentDocument).document,t.write(),t.close(),n=j(e,t),lt.detach()),ut[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in St)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Tt.length;n--;)if(e=Tt[n]+t,e in St)return e}function q(e,t){for(var n,i,r,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(e){return!1}if(!de.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?le[ue.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(me,"ms-").replace(ge,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var i,r=0;if(n(e))for(i=e.length;rC.cacheLength&&delete e[t.shift()],e[n+" "]=i}var t=[];return e}function i(e){return e[O]=!0,e}function r(e){var t=H.createElement("div");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),i=n.length;i--;)C.attrHandle[n[i]]=t}function a(e,t){var n=t&&e,i=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(i)return i;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return i(function(t){return t=+t,i(function(n,i){for(var r,o=e([],n.length,t),a=o.length;a--;)n[r=o[a]]&&(n[r]=!(i[r]=n[r]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function d(){}function f(e){for(var t=0,n=e.length,i="";t1?function(t,n,i){for(var r=e.length;r--;)if(!e[r](t,n,i))return!1;return!0}:e[0]}function m(e,n,i){for(var r=0,o=n.length;r-1&&(i[u]=!(a[u]=d))}}else b=g(b===a?b.splice(h,b.length):b),o?o(null,a,b,l):K.apply(a,b)})}function v(e){for(var t,n,i,r=e.length,o=C.relative[e[0].type],a=o||C.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),u=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,i){var r=!o&&(i||n!==N)||((t=n).nodeType?l(e,n,i):u(e,n,i));return t=null,r}];s1&&h(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(i,a,s,l,u){var c,d,f,p=0,h="0",m=i&&[],y=[],v=N,b=i||o&&C.find.TAG("*",u),x=W+=null==v?1:Math.random()||.1,w=b.length;for(u&&(N=a===H||a||u);h!==w&&null!=(c=b[h]);h++){if(o&&c){for(d=0,a||c.ownerDocument===H||(L(c),s=!R);f=e[d++];)if(f(c,a||H,s)){l.push(c);break}u&&(W=x)}r&&((c=!f&&c)&&p--,i&&m.push(c))}if(p+=h,r&&h!==p){for(d=0;f=n[d++];)f(m,y,a,s);if(i){if(p>0)for(;h--;)m[h]||y[h]||(y[h]=Y.call(l));y=g(y)}K.apply(l,y),u&&!i&&y.length>0&&p+n.length>1&&t.uniqueSort(l)}return u&&(W=x,N=v),m};return r?i(a):a}var x,w,C,T,S,E,k,A,N,j,D,L,H,q,R,F,M,_,P,O="sizzle"+1*new Date,I=e.document,W=0,B=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(D=!0),0},V=1<<31,J={}.hasOwnProperty,G=[],Y=G.pop,Q=G.push,K=G.push,Z=G.slice,ee=function(e,t){for(var n=0,i=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),de=new RegExp(oe),fe=new RegExp("^"+ie+"$"),pe={ID:new RegExp("^#("+ie+")"),CLASS:new RegExp("^\\.("+ie+")"),TAG:new RegExp("^("+ie+"|[*])"),ATTR:new RegExp("^"+re),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,me=/^h\d$/i,ge=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,be=/'|\\/g,xe=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var i="0x"+t-65536;return i!==i||n?t:i<0?String.fromCharCode(i+65536):String.fromCharCode(i>>10|55296,1023&i|56320)},Ce=function(){L()};try{K.apply(G=Z.call(I.childNodes),I.childNodes),G[I.childNodes.length].nodeType}catch(e){K={apply:G.length?function(e,t){Q.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,i=0;e[n++]=t[i++];);e.length=n-1}}}w=t.support={},S=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,i=e?e.ownerDocument||e:I;return i!==H&&9===i.nodeType&&i.documentElement?(H=i,q=H.documentElement,R=!S(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Ce,!1):n.attachEvent&&n.attachEvent("onunload",Ce)),w.attributes=r(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=r(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=ge.test(H.getElementsByClassName),w.getById=r(function(e){return q.appendChild(e).id=O,!H.getElementsByName||!H.getElementsByName(O).length}),w.getById?(C.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&R){var n=t.getElementById(e);return n?[n]:[]}},C.filter.ID=function(e){var t=e.replace(xe,we);return function(e){return e.getAttribute("id")===t}}):(delete C.find.ID,C.filter.ID=function(e){var t=e.replace(xe,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),C.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,i=[],r=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[r++];)1===n.nodeType&&i.push(n);return i}return o},C.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&R)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=ge.test(H.querySelectorAll))&&(r(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+O+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+O+"+*").length||F.push(".#.+[+~]")}),r(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=ge.test(_=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&r(function(e){w.disconnectedMatch=_.call(e,"div"),_.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=ge.test(q.compareDocumentPosition),P=t||ge.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,i=t&&t.parentNode;return e===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):e.compareDocumentPosition&&16&e.compareDocumentPosition(i)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return D=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===I&&P(I,e)?-1:t===H||t.ownerDocument===I&&P(I,t)?1:j?ee(j,e)-ee(j,t):0:4&n?-1:1)}:function(e,t){if(e===t)return D=!0,0;var n,i=0,r=e.parentNode,o=t.parentNode,s=[e],l=[t];if(!r||!o)return e===H?-1:t===H?1:r?-1:o?1:j?ee(j,e)-ee(j,t):0;if(r===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)l.unshift(n);for(;s[i]===l[i];)i++;return i?a(s[i],l[i]):s[i]===I?-1:l[i]===I?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&R&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var i=_.call(e,n);if(i||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return i}catch(e){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),P(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=C.attrHandle[t.toLowerCase()],i=n&&J.call(C.attrHandle,t.toLowerCase())?n(e,t,!R):void 0;return void 0!==i?i:w.attributes||!R?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],i=0,r=0;if(D=!w.detectDuplicates,j=!w.sortStable&&e.slice(0),e.sort(U),D){for(;t=e[r++];)t===e[r]&&(i=n.push(r));for(;i--;)e.splice(n[i],1)}return j=null,e},T=t.getText=function(e){var t,n="",i=0,r=e.nodeType;if(r){if(1===r||9===r||11===r){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=T(e)}else if(3===r||4===r)return e.nodeValue}else for(;t=e[i++];)n+=T(t);return n},C=t.selectors={cacheLength:50,createPseudo:i,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xe,we),e[3]=(e[3]||e[4]||e[5]||"").replace(xe,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&de.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(xe,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,i){return function(r){var o=t.attr(r,e);return null==o?"!="===n:!n||(o+="","="===n?o===i:"!="===n?o!==i:"^="===n?i&&0===o.indexOf(i):"*="===n?i&&o.indexOf(i)>-1:"$="===n?i&&o.slice(-i.length)===i:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(i)>-1:"|="===n&&(o===i||o.slice(0,i.length+1)===i+"-"))}},CHILD:function(e,t,n,i,r){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===i&&0===r?function(e){return!!e.parentNode}:function(t,n,l){var u,c,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s,b=!1;if(g){if(o){for(;m;){for(f=t;f=f[m];)if(s?f.nodeName.toLowerCase()===y:1===f.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&v){for(f=g,d=f[O]||(f[O]={}),c=d[f.uniqueID]||(d[f.uniqueID]={}),u=c[e]||[],p=u[0]===W&&u[1],b=p&&u[2],f=p&&g.childNodes[p];f=++p&&f&&f[m]||(b=p=0)||h.pop();)if(1===f.nodeType&&++b&&f===t){c[e]=[W,p,b];break}}else if(v&&(f=t,d=f[O]||(f[O]={}),c=d[f.uniqueID]||(d[f.uniqueID]={}),u=c[e]||[],p=u[0]===W&&u[1],b=p),b===!1)for(;(f=++p&&f&&f[m]||(b=p=0)||h.pop())&&((s?f.nodeName.toLowerCase()!==y:1!==f.nodeType)||!++b||(v&&(d=f[O]||(f[O]={}),c=d[f.uniqueID]||(d[f.uniqueID]={}),c[e]=[W,b]),f!==t)););return b-=r,b===i||b%i===0&&b/i>=0}}},PSEUDO:function(e,n){var r,o=C.pseudos[e]||C.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[O]?o(n):o.length>1?(r=[e,e,"",n],C.setFilters.hasOwnProperty(e.toLowerCase())?i(function(e,t){for(var i,r=o(e,n),a=r.length;a--;)i=ee(e,r[a]),e[i]=!(t[i]=r[a]); +}):function(e){return o(e,0,r)}):o}},pseudos:{not:i(function(e){var t=[],n=[],r=k(e.replace(se,"$1"));return r[O]?i(function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:i(function(e){return function(n){return t(e,n).length>0}}),contains:i(function(e){return e=e.replace(xe,we),function(t){return(t.textContent||t.innerText||T(t)).indexOf(e)>-1}}),lang:i(function(e){return fe.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(xe,we).toLowerCase(),function(t){var n;do if(n=R?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!C.pseudos.empty(e)},header:function(e){return me.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[n<0?n+t:n]}),even:u(function(e,t){for(var n=0;n=0;)e.push(i);return e}),gt:u(function(e,t,n){for(var i=n<0?n+t:n;++i2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&R&&C.relative[o[1].type]){if(t=(C.find.ID(a.matches[0].replace(xe,we),t)||[])[0],!t)return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(r=pe.needsContext.test(e)?0:o.length;r--&&(a=o[r],!C.relative[s=a.type]);)if((l=C.find[s])&&(i=l(a.matches[0].replace(xe,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(r,1),e=i.length&&f(o),!e)return K.apply(n,i),n;break}}return(u||k(e,d))(i,t,!R,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=O.split("").sort(U).join("")===O,w.detectDuplicates=!!D,L(),w.sortDetached=r(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),r(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&r(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),r(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var i;if(!n)return e[t]===!0?t.toLowerCase():(i=e.getAttributeNode(t))&&i.specified?i.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var be=function(e,t,n){for(var i=[],r=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(r&&pe(e).is(n))break;i.push(e)}return i},xe=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Ce=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Te=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===i.nodeType?pe.find.matchesSelector(i,e)?[i]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],i=this,r=i.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(i(this,e||[],!1))},not:function(e){return this.pushStack(i(this,e||[],!0))},is:function(e){return!!i(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Se,Ee=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var i,r;if(!e)return this;if(n=n||Se,"string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ee.exec(e),!i||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:ie,!0)),Ce.test(i[1])&&pe.isPlainObject(t))for(i in t)pe.isFunction(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}if(r=ie.getElementById(i[2]),r&&r.parentNode){if(r.id!==i[2])return Se.find(e);this.length=1,this[0]=r}return this.context=ie,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Se=pe(ie);var Ae=/^(?:parents|prev(?:Until|All))/,Ne={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),i=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return be(e,"parentNode")},parentsUntil:function(e,t,n){return be(e,"parentNode",n)},next:function(e){return r(e,"nextSibling")},prev:function(e){return r(e,"previousSibling")},nextAll:function(e){return be(e,"nextSibling")},prevAll:function(e){return be(e,"previousSibling")},nextUntil:function(e,t,n){return be(e,"nextSibling",n)},prevUntil:function(e,t,n){return be(e,"previousSibling",n)},siblings:function(e){return xe((e.parentNode||{}).firstChild,e)},children:function(e){return xe(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,i){var r=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(i=n),i&&"string"==typeof i&&(r=pe.filter(i,r)),this.length>1&&(Ne[e]||(r=pe.uniqueSort(r)),Ae.test(e)&&(r=r.reverse())),this.pushStack(r)}});var je=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,i,r,a=[],s=[],l=-1,u=function(){for(r=e.once,i=t=!0;s.length;l=-1)for(n=s.shift();++l-1;)a.splice(n,1),n<=l&&l--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return r=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return r=!0,n||c.disable(),this},locked:function(){return!!r},fireWith:function(e,n){return r||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||u()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!i}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",i={state:function(){return n},always:function(){return r.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];r[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===i?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,i):i}},r={};return i.pipe=i.then,pe.each(t,function(e,o){var a=o[2],s=o[3];i[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),r[o[0]]=function(){return r[o[0]+"With"](this===r?i:this,arguments),this},r[o[0]+"With"]=a.fireWith}),i.promise(r),e&&e.call(r,r),r},when:function(e){var t,n,i,r=0,o=re.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,l=1===s?e:pe.Deferred(),u=function(e,n,i){return function(r){n[e]=this,i[e]=arguments.length>1?re.call(arguments):r,i===t?l.notifyWith(n,i):--s||l.resolveWith(n,i)}};if(a>1)for(t=new Array(a),n=new Array(a),i=new Array(a);r0||(De.resolveWith(ie,[pe]),pe.fn.triggerHandler&&(pe(ie).triggerHandler("ready"),pe(ie).off("ready"))))}}),pe.ready.promise=function(t){if(!De)if(De=pe.Deferred(),"complete"===ie.readyState||"loading"!==ie.readyState&&!ie.documentElement.doScroll)e.setTimeout(pe.ready);else if(ie.addEventListener)ie.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{ie.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&ie.documentElement}catch(e){}n&&n.doScroll&&!function t(){if(!pe.isReady){try{n.doScroll("left")}catch(n){return e.setTimeout(t,50)}a(),pe.ready()}}()}return De.promise(t)},pe.ready.promise();var Le;for(Le in pe(de))break;de.ownFirst="0"===Le,de.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,i;n=ie.getElementsByTagName("body")[0],n&&n.style&&(t=ie.createElement("div"),i=ie.createElement("div"),i.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(i).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",de.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(i))}),function(){var e=ie.createElement("div");de.deleteExpando=!0;try{delete e.test}catch(e){de.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Re=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!u(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return d(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return d(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,i,r,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(r=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(i=a[n].name,0===i.indexOf("data-")&&(i=pe.camelCase(i.slice(5)),l(o,i,r[i])));pe._data(o,"parsedAttrs",!0)}return r}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?l(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var i;if(e)return t=(t||"fx")+"queue",i=pe._data(e,t),n&&(!i||pe.isArray(n)?i=pe._data(e,t,pe.makeArray(n)):i.push(n)),i||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),i=n.length,r=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===r&&(r=n.shift(),i--),r&&("fx"===t&&n.unshift("inprogress"),delete o.stop,r.call(e,a,o)),!i&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
a",de.leadingWhitespace=3===e.firstChild.nodeType,de.tbody=!e.getElementsByTagName("tbody").length,de.htmlSerialize=!!e.getElementsByTagName("link").length,de.html5Clone="<:nav>"!==ie.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),de.appendChecked=n.checked,e.innerHTML="",de.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=ie.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),de.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,de.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,de.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:de.htmlSerialize?[0,"",""]:[1,"X
","
"]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=r?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:pe.makeArray(n,[t]),u=pe.event.special[p]||{},r||!u.trigger||u.trigger.apply(i,n)!==!1)){if(!r&&!u.noBubble&&!pe.isWindow(i)){for(l=u.delegateType||p,Qe.test(l+p)||(s=s.parentNode);s;s=s.parentNode)f.push(s),c=s;c===(i.ownerDocument||ie)&&f.push(c.defaultView||c.parentWindow||e)}for(d=0;(s=f[d++])&&!t.isPropagationStopped();)t.type=d>1?l:u.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!r&&!t.isDefaultPrevented()&&(!u._default||u._default.apply(f.pop(),n)===!1)&&He(i)&&a&&i[p]&&!pe.isWindow(i)){c=i[a],c&&(i[a]=null),pe.event.triggered=p;try{i[p]()}catch(e){}pe.event.triggered=void 0,c&&(i[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,i,r,o,a=[],s=re.call(arguments),l=(pe._data(this,"events")||{})[e.type]||[],u=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,l),t=0;(r=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=r.elem,n=0;(o=r.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,i=((pe.event.special[o.origType]||{}).handle||o.handler).apply(r.elem,s),void 0!==i&&(e.result=i)===!1&&(e.preventDefault(),e.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,i,r,o,a=[],s=t.delegateCount,l=e.target;if(s&&l.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(i=[],n=0;n-1:pe.find(r,this,null,[l]).length),i[r]&&i.push(o);i.length&&a.push({elem:l,handlers:i})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(ie),st=at.appendChild(ie.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var i,r,o,a,s,l=pe.contains(e.ownerDocument,e);if(de.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(de.noCloneEvent&&de.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(i=h(o),s=h(e),a=0;null!=(r=s[a]);++a)i[a]&&k(r,i[a]);if(t)if(n)for(s=s||h(e),i=i||h(o),a=0;null!=(r=s[a]);a++)E(r,i[a]);else E(e,o);return i=h(o,"script"),i.length>0&&m(i,!l&&h(e,"script")),i=s=r=null,o},cleanData:function(e,t){for(var n,i,r,o,a=0,s=pe.expando,l=pe.cache,u=de.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(r=n[s],o=r&&l[r])){if(o.events)for(i in o.events)c[i]?pe.event.remove(n,i):pe.removeEvent(n,i,o.handle);l[r]&&(delete l[r],u||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(r))}}}),pe.fn.extend({domManip:A,detach:function(e){return N(this,e,!0)},remove:function(e){return N(this,e)},text:function(e){return Oe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||ie).createTextNode(e))},null,e,arguments.length)},append:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=C(this,e);t.appendChild(e)}})},prepend:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=C(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Oe(this,function(e){var t=this[0]||{},n=0,i=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(de.htmlSerialize||!et.test(e))&&(de.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=u.getElementsByTagName("td"), +t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),d.removeChild(l)}var n,i,r,o,a,s,l=ie.createElement("div"),u=ie.createElement("div");u.style&&(u.style.cssText="float:left;opacity:.5",de.opacity="0.5"===u.style.opacity,de.cssFloat=!!u.style.cssFloat,u.style.backgroundClip="content-box",u.cloneNode(!0).style.backgroundClip="",de.clearCloneStyle="content-box"===u.style.backgroundClip,l=ie.createElement("div"),l.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",u.innerHTML="",l.appendChild(u),de.boxSizing=""===u.style.boxSizing||""===u.style.MozBoxSizing||""===u.style.WebkitBoxSizing,pe.extend(de,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),r},pixelMarginRight:function(){return null==n&&t(),i},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,mt,gt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},mt=function(e,t,n){var i,r,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!de.pixelMarginRight()&&dt.test(a)&&ct.test(t)&&(i=s.width,r=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=i,s.minWidth=r,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},mt=function(e,t,n){var i,r,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),dt.test(a)&&!gt.test(t)&&(i=s.left,r=e.runtimeStyle,o=r&&r.left,o&&(r.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=i,o&&(r.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,bt=/^(none|table(?!-c[ea]).+)/,xt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Ct={letterSpacing:"0",fontWeight:"400"},Tt=["Webkit","O","Moz","ms"],St=ie.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=mt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":de.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var r,o,a,s=pe.camelCase(t),l=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(r=a.get(e,!1,i))?r:l[t];if(o=typeof n,"string"===o&&(r=Me.exec(n))&&r[1]&&(n=f(e,t,r),o="number"),null!=n&&n===n&&("number"===o&&(n+=r&&r[3]||(pe.cssNumber[s]?"":"px")),de.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,i)))))try{l[t]=n}catch(e){}}},css:function(e,t,n,i){var r,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=mt(e,t,i)),"normal"===o&&t in Ct&&(o=Ct[t]),""===n||n?(r=parseFloat(o),n===!0||isFinite(r)?r||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,i){if(n)return bt.test(pe.css(e,"display"))&&0===e.offsetWidth?ft(e,wt,function(){return M(e,t,i)}):M(e,t,i)},set:function(e,n,i){var r=i&&ht(e);return R(e,n,i?F(e,t,i,de.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,r),r):0)}}}),de.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,i=e.currentStyle,r=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=i&&i.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||i&&!i.filter)||(n.filter=yt.test(o)?o.replace(yt,r):o+" "+r)}}),pe.cssHooks.marginRight=L(de.reliableMarginRight,function(e,t){if(t)return ft(e,{display:"inline-block"},mt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(de.reliableMarginLeft,function(e,t){if(t)return(parseFloat(mt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-ft(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var i=0,r={},o="string"==typeof n?n.split(" "):[n];i<4;i++)r[e+_e[i]+t]=o[i]||o[i-2]||o[0];return r}},ct.test(e)||(pe.cssHooks[e+t].set=R)}),pe.fn.extend({css:function(e,t){return Oe(this,function(e,t,n){var i,r,o={},a=0;if(pe.isArray(t)){for(i=ht(e),r=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Pe(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=_,_.prototype={constructor:_,init:function(e,t,n,i,r,o){this.elem=e,this.prop=n,this.easing=r||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=i,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=_.propHooks[this.prop];return e&&e.get?e.get(this):_.propHooks._default.get(this)},run:function(e){var t,n=_.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):_.propHooks._default.set(this),this}},_.prototype.init.prototype=_.prototype,_.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},_.propHooks.scrollTop=_.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=_.prototype.init,pe.fx.step={};var Et,kt,At=/^(?:toggle|show|hide)$/,Nt=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return f(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(je);for(var n,i=0,r=e.length;i
a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",de.getSetAttribute="t"!==n.className,de.style=/top/.test(e.getAttribute("style")),de.hrefNormalized="/a"===e.getAttribute("href"),de.checkOn=!!t.value,de.optSelected=r.selected,de.enctype=!!ie.createElement("form").enctype,i.disabled=!0,de.optDisabled=!r.disabled,t=ie.createElement("input"),t.setAttribute("value",""),de.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),de.radioValue="t"===t.value}();var jt=/\r/g;pe.fn.extend({val:function(e){var t,n,i,r=this[0];{if(arguments.length)return i=pe.isFunction(e),this.each(function(n){var r;1===this.nodeType&&(r=i?e.call(this,n,pe(this).val()):e,null==r?r="":"number"==typeof r?r+="":pe.isArray(r)&&(r=pe.map(r,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,r,"value")||(this.value=r))});if(r)return t=pe.valHooks[r.type]||pe.valHooks[r.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(r,"value"))?n:(n=r.value,"string"==typeof n?n.replace(jt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e))}},select:{get:function(e){for(var t,n,i=e.options,r=e.selectedIndex,o="select-one"===e.type||r<0,a=o?null:[],s=o?r+1:i.length,l=r<0?s:o?r:0;l=0)try{i.selected=n=!0}catch(e){i.scrollHeight}else i.selected=!1;return n||(e.selectedIndex=-1),r}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},de.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Dt,Lt,Ht=pe.expr.attrHandle,qt=/^(?:checked|selected)$/i,Rt=de.getSetAttribute,Ft=de.input;pe.fn.extend({attr:function(e,t){return Oe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var i,r,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),r=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Lt:Dt)),void 0!==n?null===n?void pe.removeAttr(e,t):r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):r&&"get"in r&&null!==(i=r.get(e,t))?i:(i=pe.find.attr(e,t),null==i?void 0:i))},attrHooks:{type:{set:function(e,t){if(!de.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,i,r=0,o=t&&t.match(je);if(o&&1===e.nodeType)for(;n=o[r++];)i=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Ft&&Rt||!qt.test(n)?e[i]=!1:e[pe.camelCase("default-"+n)]=e[i]=!1:pe.attr(e,n,""),e.removeAttribute(Rt?n:i)}}),Lt={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Ft&&Rt||!qt.test(n)?e.setAttribute(!Rt&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Ht[t]||pe.find.attr;Ft&&Rt||!qt.test(t)?Ht[t]=function(e,t,i){var r,o;return i||(o=Ht[t],Ht[t]=r,r=null!=n(e,t,i)?t.toLowerCase():null,Ht[t]=o),r}:Ht[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Ft&&Rt||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Dt&&Dt.set(e,t,n)}}),Rt||(Dt={set:function(e,t,n){var i=e.getAttributeNode(n);if(i||e.setAttributeNode(i=e.ownerDocument.createAttribute(n)),i.value=t+="","value"===n||t===e.getAttribute(n))return t}},Ht.id=Ht.name=Ht.coords=function(e,t,n){var i;if(!n)return(i=e.getAttributeNode(t))&&""!==i.value?i.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Dt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Dt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),de.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Mt=/^(?:input|select|textarea|button|object)$/i,_t=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Oe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(e){}})}}),pe.extend({prop:function(e,t,n){var i,r,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,r=pe.propHooks[t]),void 0!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:e[t]=n:r&&"get"in r&&null!==(i=r.get(e,t))?i:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Mt.test(e.nodeName)||_t.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),de.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),de.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),de.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,i,r,o,a,s,l=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(je)||[];n=this[l++];)if(r=z(n),i=1===n.nodeType&&(" "+r+" ").replace(Pt," ")){for(a=0;o=t[a++];)i.indexOf(" "+o+" ")<0&&(i+=o+" ");s=pe.trim(i),r!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,i,r,o,a,s,l=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(je)||[];n=this[l++];)if(r=z(n),i=1===n.nodeType&&(" "+r+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;i.indexOf(" "+o+" ")>-1;)i=i.replace(" "+o+" "," ");s=pe.trim(i),r!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,i,r,o;if("string"===n)for(i=0,r=pe(this),o=e.match(je)||[];t=o[i++];)r.hasClass(t)?r.removeClass(t):r.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,i=0;for(t=" "+e+" ";n=this[i++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Ot=e.location,It=pe.now(),Wt=/\?/,Bt=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,i=null,r=pe.trim(t+"");return r&&!pe.trim(r.replace(Bt,function(e,t,r,o){return n&&t&&(i=0),0===i?e:(n=r||t,i+=!o-!r,"")}))?Function("return "+r)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,i;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(i=new e.DOMParser,n=i.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(e){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var $t=/#.*$/,zt=/([?&])_=[^&]*/,Xt=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Ut=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Vt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Yt={},Qt={},Kt="*/".concat("*"),Zt=Ot.href,en=Gt.exec(Zt.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Zt,type:"GET",isLocal:Ut.test(en[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Yt),ajaxTransport:X(Qt),ajax:function(t,n){function i(t,n,i,r){var o,d,v,b,w,T=n;2!==x&&(x=2,l&&e.clearTimeout(l),c=void 0,s=r||"",C.readyState=t>0?4:0,o=t>=200&&t<300||304===t,i&&(b=J(f,C,i)),b=G(f,b,C,o),o?(f.ifModified&&(w=C.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=C.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===f.type?T="nocontent":304===t?T="notmodified":(T=b.state,d=b.data,v=b.error,o=!v)):(v=T,!t&&T||(T="error",t<0&&(t=0))),C.status=t,C.statusText=(n||T)+"",o?m.resolveWith(p,[d,T,C]):m.rejectWith(p,[C,T,v]),C.statusCode(y),y=void 0,u&&h.trigger(o?"ajaxSuccess":"ajaxError",[C,f,o?d:v]),g.fireWith(p,[C,T]),u&&(h.trigger("ajaxComplete",[C,f]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var r,o,a,s,l,u,c,d,f=pe.ajaxSetup({},n),p=f.context||f,h=f.context&&(p.nodeType||p.jquery)?pe(p):pe.event,m=pe.Deferred(),g=pe.Callbacks("once memory"),y=f.statusCode||{},v={},b={},x=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!d)for(d={};t=Xt.exec(s);)d[t[1].toLowerCase()]=t[2];t=d[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=b[n]=b[n]||e,v[e]=t),this},overrideMimeType:function(e){return x||(f.mimeType=e),this},statusCode:function(e){var t;if(e)if(x<2)for(t in e)y[t]=[y[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),i(0,t),this}};if(m.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,f.url=((t||f.url||Zt)+"").replace($t,"").replace(Jt,en[1]+"//"),f.type=n.method||n.type||f.method||f.type,f.dataTypes=pe.trim(f.dataType||"*").toLowerCase().match(je)||[""],null==f.crossDomain&&(r=Gt.exec(f.url.toLowerCase()),f.crossDomain=!(!r||r[1]===en[1]&&r[2]===en[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(en[3]||("http:"===en[1]?"80":"443")))),f.data&&f.processData&&"string"!=typeof f.data&&(f.data=pe.param(f.data,f.traditional)),U(Yt,f,n,C),2===x)return C;u=pe.event&&f.global,u&&0===pe.active++&&pe.event.trigger("ajaxStart"),f.type=f.type.toUpperCase(),f.hasContent=!Vt.test(f.type),a=f.url,f.hasContent||(f.data&&(a=f.url+=(Wt.test(a)?"&":"?")+f.data,delete f.data),f.cache===!1&&(f.url=zt.test(a)?a.replace(zt,"$1_="+It++):a+(Wt.test(a)?"&":"?")+"_="+It++)),f.ifModified&&(pe.lastModified[a]&&C.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&C.setRequestHeader("If-None-Match",pe.etag[a])),(f.data&&f.hasContent&&f.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",f.contentType),C.setRequestHeader("Accept",f.dataTypes[0]&&f.accepts[f.dataTypes[0]]?f.accepts[f.dataTypes[0]]+("*"!==f.dataTypes[0]?", "+Kt+"; q=0.01":""):f.accepts["*"]);for(o in f.headers)C.setRequestHeader(o,f.headers[o]);if(f.beforeSend&&(f.beforeSend.call(p,C,f)===!1||2===x))return C.abort();w="abort";for(o in{success:1,error:1,complete:1})C[o](f[o]);if(c=U(Qt,f,n,C)){if(C.readyState=1,u&&h.trigger("ajaxSend",[C,f]),2===x)return C;f.async&&f.timeout>0&&(l=e.setTimeout(function(){C.abort("timeout")},f.timeout));try{x=1,c.send(v,i)}catch(e){if(!(x<2))throw e;i(-1,e)}}else i(-1,"No Transport");return C},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,i,r){return pe.isFunction(n)&&(r=r||i,i=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:r,data:n,success:i},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return de.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:Q(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var tn=/%20/g,nn=/\[\]$/,rn=/\r?\n/g,on=/^(?:submit|button|image|reset|file)$/i,an=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,i=[],r=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){r(this.name,this.value)});else for(n in e)K(n,e[n],t,r);return i.join("&").replace(tn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&an.test(this.nodeName)&&!on.test(e)&&(this.checked||!Ie.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(rn,"\r\n")}}):{name:t.name,value:n.replace(rn,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():ie.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var sn=0,ln={},un=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),de.cors=!!un&&"withCredentials"in un,un=de.ajax=!!un,un&&pe.ajaxTransport(function(t){if(!t.crossDomain||de.cors){var n;return{send:function(i,r){var o,a=t.xhr(),s=++sn;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(o in i)void 0!==i[o]&&a.setRequestHeader(o,i[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,i){var o,l,u;if(n&&(i||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,i)4!==a.readyState&&a.abort();else{u={},o=a.status,"string"==typeof a.responseText&&(u.text=a.responseText);try{l=a.statusText}catch(e){l=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=u.text?200:404}u&&r(o,l,u,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=ie.head||pe("head")[0]||ie.documentElement;return{send:function(i,r){t=ie.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||r(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var cn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=cn.pop()||pe.expando+"_"+It++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,i){var r,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return r=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+r):t.jsonp!==!1&&(t.url+=(Wt.test(t.url)?"&":"?")+t.jsonp+"="+r),t.converters["script json"]=function(){return a||pe.error(r+" was not called"),a[0]},t.dataTypes[0]="json",o=e[r],e[r]=function(){a=arguments},i.always(function(){void 0===o?pe(e).removeProp(r):e[r]=o,t[r]&&(t.jsonpCallback=n.jsonpCallback,cn.push(r)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),de.createHTMLDocument=function(){if(!ie.implementation.createHTMLDocument)return!1;var e=ie.implementation.createHTMLDocument("");return e.body.innerHTML="
",2===e.body.childNodes.length}(),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||(de.createHTMLDocument?ie.implementation.createHTMLDocument(""):ie);var i=Ce.exec(e),r=!n&&[];return i?[t.createElement(i[1])]:(i=y([e],t,r),r&&r.length&&pe(r).remove(),pe.merge([],i.childNodes))};var fn=pe.fn.load;pe.fn.load=function(e,t,n){if("string"!=typeof e&&fn)return fn.apply(this,arguments);var i,r,o,a=this,s=e.indexOf(" ");return s>-1&&(i=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(r="POST"),a.length>0&&pe.ajax({url:e,type:r||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(i?pe("
").append(pe.parseHTML(e)).find(i):e)}).always(n&&function(e,t){a.each(function(){n.apply(a,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var i,r,o,a,s,l,u,c=pe.css(e,"position"),d=pe(e),f={};"static"===c&&(e.style.position="relative"),s=d.offset(),o=pe.css(e,"top"),l=pe.css(e,"left"),u=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,l])>-1,u?(i=d.position(),a=i.top,r=i.left):(a=parseFloat(o)||0,r=parseFloat(l)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+r),"using"in t?t.using.call(e,f):d.css(f)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,i={top:0,left:0},r=this[0],o=r&&r.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,r)?("undefined"!=typeof r.getBoundingClientRect&&(i=r.getBoundingClientRect()),n=te(o),{top:i.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:i.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):i},position:function(){if(this[0]){var e,t,n={top:0,left:0},i=this[0];return"fixed"===pe.css(i,"position")?t=i.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(i,"marginTop",!0),left:t.left-n.left-pe.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(i){return Oe(this,function(e,i,r){var o=te(e);return void 0===r?o?t in o?o[t]:o.document.documentElement[i]:e[i]:void(o?o.scrollTo(n?pe(o).scrollLeft():r,n?r:pe(o).scrollTop()):e[i]=r)},e,i,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(de.pixelPosition,function(e,n){if(n)return n=mt(e,t),dt.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,i){pe.fn[i]=function(i,r){var o=arguments.length&&(n||"boolean"!=typeof i),a=n||(i===!0||r===!0?"margin":"border");return Oe(this,function(t,n,i){var r;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(r=t.documentElement,Math.max(t.body["scroll"+e],r["scroll"+e],t.body["offset"+e],r["offset"+e],r["client"+e])):void 0===i?pe.css(t,n,a):pe.style(t,n,i,a)},t,o?i:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,i){return this.on(t,e,n,i)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return pe});var pn=e.jQuery,hn=e.$;return pe.noConflict=function(t){return e.$===pe&&(e.$=hn),t&&e.jQuery===pe&&(e.jQuery=pn),pe},t||(e.jQuery=e.$=pe),pe}),function(e,t){"use strict";e.rails!==t&&e.error("jquery-ujs has already been loaded!");var n,i=e(document);e.rails=n={linkClickSelector:"a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]",buttonClickSelector:"button[data-remote]:not([form]):not(form button), button[data-confirm]:not([form]):not(form button)",inputChangeSelector:"select[data-remote], input[data-remote], textarea[data-remote]", +formSubmitSelector:"form",formInputClickSelector:"form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])",disableSelector:"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled",enableSelector:"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled",requiredInputSelector:"input[name][required]:not([disabled]), textarea[name][required]:not([disabled])",fileInputSelector:"input[type=file]:not([disabled])",linkDisableSelector:"a[data-disable-with], a[data-disable]",buttonDisableSelector:"button[data-remote][data-disable-with], button[data-remote][data-disable]",csrfToken:function(){return e("meta[name=csrf-token]").attr("content")},csrfParam:function(){return e("meta[name=csrf-param]").attr("content")},CSRFProtection:function(e){var t=n.csrfToken();t&&e.setRequestHeader("X-CSRF-Token",t)},refreshCSRFTokens:function(){e('form input[name="'+n.csrfParam()+'"]').val(n.csrfToken())},fire:function(t,n,i){var r=e.Event(n);return t.trigger(r,i),r.result!==!1},confirm:function(e){return confirm(e)},ajax:function(t){return e.ajax(t)},href:function(e){return e[0].href},isRemote:function(e){return e.data("remote")!==t&&e.data("remote")!==!1},handleRemote:function(i){var r,o,a,s,l,u;if(n.fire(i,"ajax:before")){if(s=i.data("with-credentials")||null,l=i.data("type")||e.ajaxSettings&&e.ajaxSettings.dataType,i.is("form")){r=i.data("ujs:submit-button-formmethod")||i.attr("method"),o=i.data("ujs:submit-button-formaction")||i.attr("action"),a=e(i[0]).serializeArray();var c=i.data("ujs:submit-button");c&&(a.push(c),i.data("ujs:submit-button",null)),i.data("ujs:submit-button-formmethod",null),i.data("ujs:submit-button-formaction",null)}else i.is(n.inputChangeSelector)?(r=i.data("method"),o=i.data("url"),a=i.serialize(),i.data("params")&&(a=a+"&"+i.data("params"))):i.is(n.buttonClickSelector)?(r=i.data("method")||"get",o=i.data("url"),a=i.serialize(),i.data("params")&&(a=a+"&"+i.data("params"))):(r=i.data("method"),o=n.href(i),a=i.data("params")||null);return u={type:r||"GET",data:a,dataType:l,beforeSend:function(e,r){return r.dataType===t&&e.setRequestHeader("accept","*/*;q=0.5, "+r.accepts.script),!!n.fire(i,"ajax:beforeSend",[e,r])&&void i.trigger("ajax:send",e)},success:function(e,t,n){i.trigger("ajax:success",[e,t,n])},complete:function(e,t){i.trigger("ajax:complete",[e,t])},error:function(e,t,n){i.trigger("ajax:error",[e,t,n])},crossDomain:n.isCrossDomain(o)},s&&(u.xhrFields={withCredentials:s}),o&&(u.url=o),n.ajax(u)}return!1},isCrossDomain:function(e){var t=document.createElement("a");t.href=location.href;var n=document.createElement("a");try{return n.href=e,n.href=n.href,!((!n.protocol||":"===n.protocol)&&!n.host||t.protocol+"//"+t.host==n.protocol+"//"+n.host)}catch(e){return!0}},handleMethod:function(i){var r=n.href(i),o=i.data("method"),a=i.attr("target"),s=n.csrfToken(),l=n.csrfParam(),u=e('
'),c='';l===t||s===t||n.isCrossDomain(r)||(c+=''),a&&u.attr("target",a),u.hide().append(c).appendTo("body"),u.submit()},formElements:function(t,n){return t.is("form")?e(t[0].elements).filter(n):t.find(n)},disableFormElements:function(t){n.formElements(t,n.disableSelector).each(function(){n.disableFormElement(e(this))})},disableFormElement:function(e){var n,i;n=e.is("button")?"html":"val",i=e.data("disable-with"),i!==t&&(e.data("ujs:enable-with",e[n]()),e[n](i)),e.prop("disabled",!0),e.data("ujs:disabled",!0)},enableFormElements:function(t){n.formElements(t,n.enableSelector).each(function(){n.enableFormElement(e(this))})},enableFormElement:function(e){var n=e.is("button")?"html":"val";e.data("ujs:enable-with")!==t&&(e[n](e.data("ujs:enable-with")),e.removeData("ujs:enable-with")),e.prop("disabled",!1),e.removeData("ujs:disabled")},allowAction:function(e){var t,i=e.data("confirm"),r=!1;if(!i)return!0;if(n.fire(e,"confirm")){try{r=n.confirm(i)}catch(e){(console.error||console.log).call(console,e.stack||e)}t=n.fire(e,"confirm:complete",[r])}return r&&t},blankInputs:function(t,n,i){var r,o,a,s,l=e(),u=n||"input,textarea",c=t.find(u),d={};return c.each(function(){r=e(this),r.is("input[type=radio]")?(s=r.attr("name"),d[s]||(0===t.find('input[type=radio]:checked[name="'+s+'"]').length&&(a=t.find('input[type=radio][name="'+s+'"]'),l=l.add(a)),d[s]=s)):(o=r.is("input[type=checkbox],input[type=radio]")?r.is(":checked"):!!r.val(),o===i&&(l=l.add(r)))}),!!l.length&&l},nonBlankInputs:function(e,t){return n.blankInputs(e,t,!0)},stopEverything:function(t){return e(t.target).trigger("ujs:everythingStopped"),t.stopImmediatePropagation(),!1},disableElement:function(e){var i=e.data("disable-with");i!==t&&(e.data("ujs:enable-with",e.html()),e.html(i)),e.bind("click.railsDisable",function(e){return n.stopEverything(e)}),e.data("ujs:disabled",!0)},enableElement:function(e){e.data("ujs:enable-with")!==t&&(e.html(e.data("ujs:enable-with")),e.removeData("ujs:enable-with")),e.unbind("click.railsDisable"),e.removeData("ujs:disabled")}},n.fire(i,"rails:attachBindings")&&(e.ajaxPrefilter(function(e,t,i){e.crossDomain||n.CSRFProtection(i)}),e(window).on("pageshow.rails",function(){e(e.rails.enableSelector).each(function(){var t=e(this);t.data("ujs:disabled")&&e.rails.enableFormElement(t)}),e(e.rails.linkDisableSelector).each(function(){var t=e(this);t.data("ujs:disabled")&&e.rails.enableElement(t)})}),i.delegate(n.linkDisableSelector,"ajax:complete",function(){n.enableElement(e(this))}),i.delegate(n.buttonDisableSelector,"ajax:complete",function(){n.enableFormElement(e(this))}),i.delegate(n.linkClickSelector,"click.rails",function(t){var i=e(this),r=i.data("method"),o=i.data("params"),a=t.metaKey||t.ctrlKey;if(!n.allowAction(i))return n.stopEverything(t);if(!a&&i.is(n.linkDisableSelector)&&n.disableElement(i),n.isRemote(i)){if(a&&(!r||"GET"===r)&&!o)return!0;var s=n.handleRemote(i);return s===!1?n.enableElement(i):s.fail(function(){n.enableElement(i)}),!1}return r?(n.handleMethod(i),!1):void 0}),i.delegate(n.buttonClickSelector,"click.rails",function(t){var i=e(this);if(!n.allowAction(i)||!n.isRemote(i))return n.stopEverything(t);i.is(n.buttonDisableSelector)&&n.disableFormElement(i);var r=n.handleRemote(i);return r===!1?n.enableFormElement(i):r.fail(function(){n.enableFormElement(i)}),!1}),i.delegate(n.inputChangeSelector,"change.rails",function(t){var i=e(this);return n.allowAction(i)&&n.isRemote(i)?(n.handleRemote(i),!1):n.stopEverything(t)}),i.delegate(n.formSubmitSelector,"submit.rails",function(i){var r,o,a=e(this),s=n.isRemote(a);if(!n.allowAction(a))return n.stopEverything(i);if(a.attr("novalidate")===t)if(a.data("ujs:formnovalidate-button")===t){if(r=n.blankInputs(a,n.requiredInputSelector,!1),r&&n.fire(a,"ajax:aborted:required",[r]))return n.stopEverything(i)}else a.data("ujs:formnovalidate-button",t);if(s){if(o=n.nonBlankInputs(a,n.fileInputSelector)){setTimeout(function(){n.disableFormElements(a)},13);var l=n.fire(a,"ajax:aborted:file",[o]);return l||setTimeout(function(){n.enableFormElements(a)},13),l}return n.handleRemote(a),!1}setTimeout(function(){n.disableFormElements(a)},13)}),i.delegate(n.formInputClickSelector,"click.rails",function(t){var i=e(this);if(!n.allowAction(i))return n.stopEverything(t);var r=i.attr("name"),o=r?{name:r,value:i.val()}:null,a=i.closest("form");0===a.length&&(a=e("#"+i.attr("form"))),a.data("ujs:submit-button",o),a.data("ujs:formnovalidate-button",i.attr("formnovalidate")),a.data("ujs:submit-button-formaction",i.attr("formaction")),a.data("ujs:submit-button-formmethod",i.attr("formmethod"))}),i.delegate(n.formSubmitSelector,"ajax:send.rails",function(t){this===t.target&&n.disableFormElements(e(this))}),i.delegate(n.formSubmitSelector,"ajax:complete.rails",function(t){this===t.target&&n.enableFormElements(e(this))}),e(function(){n.refreshCSRFTokens()}))}(jQuery),function(){var e=[].slice;this.ActionCable={INTERNAL:{message_types:{welcome:"welcome",ping:"ping",confirmation:"confirm_subscription",rejection:"reject_subscription"},default_mount_path:"/cable",protocols:["actioncable-v1-json","actioncable-unsupported"]},createConsumer:function(e){var t;return null==e&&(e=null!=(t=this.getConfig("url"))?t:this.INTERNAL.default_mount_path),new ActionCable.Consumer(this.createWebSocketURL(e))},getConfig:function(e){var t;return t=document.head.querySelector("meta[name='action-cable-"+e+"']"),null!=t?t.getAttribute("content"):void 0},createWebSocketURL:function(e){var t;return e&&!/^wss?:/i.test(e)?(t=document.createElement("a"),t.href=e,t.href=t.href,t.protocol=t.protocol.replace("http","ws"),t.href):e},startDebugging:function(){return this.debugging=!0},stopDebugging:function(){return this.debugging=null},log:function(){var t;if(t=1<=arguments.length?e.call(arguments,0):[],this.debugging)return t.push(Date.now()),console.log.apply(console,["[ActionCable]"].concat(e.call(t)))}},"undefined"!=typeof window&&null!==window&&(window.ActionCable=this.ActionCable),"undefined"!=typeof module&&null!==module&&(module.exports=this.ActionCable)}.call(this),function(){var e=function(e,t){return function(){return e.apply(t,arguments)}};ActionCable.ConnectionMonitor=function(){function t(t){this.connection=t,this.visibilityDidChange=e(this.visibilityDidChange,this),this.reconnectAttempts=0}var n,i,r;return t.pollInterval={min:3,max:30},t.staleThreshold=6,t.prototype.start=function(){if(!this.isRunning())return this.startedAt=i(),delete this.stoppedAt,this.startPolling(),document.addEventListener("visibilitychange",this.visibilityDidChange),ActionCable.log("ConnectionMonitor started. pollInterval = "+this.getPollInterval()+" ms")},t.prototype.stop=function(){if(this.isRunning())return this.stoppedAt=i(),this.stopPolling(),document.removeEventListener("visibilitychange",this.visibilityDidChange),ActionCable.log("ConnectionMonitor stopped")},t.prototype.isRunning=function(){return null!=this.startedAt&&null==this.stoppedAt},t.prototype.recordPing=function(){return this.pingedAt=i()},t.prototype.recordConnect=function(){return this.reconnectAttempts=0,this.recordPing(),delete this.disconnectedAt,ActionCable.log("ConnectionMonitor recorded connect")},t.prototype.recordDisconnect=function(){return this.disconnectedAt=i(),ActionCable.log("ConnectionMonitor recorded disconnect")},t.prototype.startPolling=function(){return this.stopPolling(),this.poll()},t.prototype.stopPolling=function(){return clearTimeout(this.pollTimeout)},t.prototype.poll=function(){return this.pollTimeout=setTimeout(function(e){return function(){return e.reconnectIfStale(),e.poll()}}(this),this.getPollInterval())},t.prototype.getPollInterval=function(){var e,t,i,r;return r=this.constructor.pollInterval,i=r.min,t=r.max,e=5*Math.log(this.reconnectAttempts+1),Math.round(1e3*n(e,i,t))},t.prototype.reconnectIfStale=function(){if(this.connectionIsStale())return ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = "+this.reconnectAttempts+", pollInterval = "+this.getPollInterval()+" ms, time disconnected = "+r(this.disconnectedAt)+" s, stale threshold = "+this.constructor.staleThreshold+" s"),this.reconnectAttempts++,this.disconnectedRecently()?ActionCable.log("ConnectionMonitor skipping reopening recent disconnect"):(ActionCable.log("ConnectionMonitor reopening"),this.connection.reopen())},t.prototype.connectionIsStale=function(){var e;return r(null!=(e=this.pingedAt)?e:this.startedAt)>this.constructor.staleThreshold},t.prototype.disconnectedRecently=function(){return this.disconnectedAt&&r(this.disconnectedAt)=0},e.prototype.isState=function(){var e,t;return t=1<=arguments.length?a.call(arguments,0):[],e=this.getState(),l.call(t,e)>=0},e.prototype.getState=function(){var e,t,n;for(t in WebSocket)if(n=WebSocket[t],n===(null!=(e=this.webSocket)?e.readyState:void 0))return t.toLowerCase();return null},e.prototype.installEventHandlers=function(){var e,t;for(e in this.events)t=this.events[e].bind(this),this.webSocket["on"+e]=t},e.prototype.uninstallEventHandlers=function(){var e;for(e in this.events)this.webSocket["on"+e]=function(){}},e.prototype.events={message:function(e){var n,i,r,o;if(this.isProtocolSupported())switch(r=JSON.parse(e.data),n=r.identifier,i=r.message,o=r.type,o){case t.welcome:return this.monitor.recordConnect(),this.subscriptions.reload();case t.ping:return this.monitor.recordPing();case t.confirmation:return this.subscriptions.notify(n,"connected");case t.rejection:return this.subscriptions.reject(n);default:return this.subscriptions.notify(n,"received",i)}},open:function(){if(ActionCable.log("WebSocket onopen event, using '"+this.getProtocol()+"' subprotocol"),this.disconnected=!1,!this.isProtocolSupported())return ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting."),this.close({allowReconnect:!1})},close:function(){if(ActionCable.log("WebSocket onclose event"),!this.disconnected)return this.disconnected=!0,this.monitor.recordDisconnect(),this.subscriptions.notifyAll("disconnected",{willAttemptReconnect:this.monitor.isRunning()})},error:function(){return ActionCable.log("WebSocket onerror event")}},e}()}.call(this),function(){var e=[].slice;ActionCable.Subscriptions=function(){function t(e){this.consumer=e,this.subscriptions=[]}return t.prototype.create=function(e,t){var n,i,r;return n=e,i="object"==typeof n?n:{channel:n},r=new ActionCable.Subscription(this.consumer,i,t),this.add(r)},t.prototype.add=function(e){return this.subscriptions.push(e),this.consumer.ensureActiveConnection(),this.notify(e,"initialized"),this.sendCommand(e,"subscribe"),e},t.prototype.remove=function(e){return this.forget(e),this.findAll(e.identifier).length||this.sendCommand(e,"unsubscribe"),e},t.prototype.reject=function(e){var t,n,i,r,o;for(i=this.findAll(e),r=[],t=0,n=i.length;t micro_post.id} + expect(response).to redirect_to user + end + end + + end +end + + diff --git a/spec/controllers/relationships_controller_spec.rb b/spec/controllers/relationships_controller_spec.rb new file mode 100644 index 000000000..8291091a7 --- /dev/null +++ b/spec/controllers/relationships_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe RelationshipsController, type: :controller do + +end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 000000000..ec4bfd4c6 --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,51 @@ +require 'rails_helper' + +describe UsersController do + + let(:user){ create(:user) } + let(:micro_post){ create(:post) } + let(:user2){ create(:user) } + + + + + describe "GET #index" do + before do + request.cookies["auth_token"] = user.auth_token + end + it "returns a number of users from a search query" do + get :index, params: {search: ""} + expect(response.status).to eq(200) + end + end + + describe "POST #create" do + it "user#create will create a new User" do + expect{post :create, params: { user: attributes_for(:user)}}.to change(User, :count).by(1) + expect(response).to redirect_to user_profiles_path(User.last) + end + + it "user#create with no attributes will not create a new User" do + expect{post :create, params: {user: {email: "fail@gmail.com"}} }.to_not change(User, :count) + end + end + + describe "GET #edit" do + + context "for signed in user" do + before do + request.cookies["auth_token"] = user.auth_token + end + + it "authorized user has access to user#edit for own edit" do + get :edit, params: {id: user.id} + expect(response).to render_template :edit + end + + it "authorized user sets right instance variable for edit" do + get :edit, params: {id: user.id} + expect(assigns(:user)).to eq(user) + end + end + end + end \ No newline at end of file diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb new file mode 100644 index 000000000..a8dd1f1ce --- /dev/null +++ b/spec/factories/comments.rb @@ -0,0 +1,10 @@ +FactoryGirl.define do + factory :comment do + content "BLAH!!" + association :commentable, factory: :post + + factory :comment_comment do + association :commentable, factory: :comment + end + end +end \ No newline at end of file diff --git a/spec/factories/photos.rb b/spec/factories/photos.rb new file mode 100644 index 000000000..6ce47ce80 --- /dev/null +++ b/spec/factories/photos.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :photo do + picture_file_name "test_photo.jpg" + picture_content_type "image/jpeg" + picture_file_size 83261 + association :profile + end +end \ No newline at end of file diff --git a/spec/factories/posts.rb b/spec/factories/posts.rb new file mode 100644 index 000000000..32f0bafb5 --- /dev/null +++ b/spec/factories/posts.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :post do + content "BLAH!!" + from 1 + association :user + end +end \ No newline at end of file diff --git a/spec/factories/relationships.rb b/spec/factories/relationships.rb new file mode 100644 index 000000000..d6c9a96e0 --- /dev/null +++ b/spec/factories/relationships.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :relationship do + + end +end diff --git "a/spec/factories/users.rb\nusers.rb\nusers.rb" "b/spec/factories/users.rb\nusers.rb\nusers.rb" new file mode 100644 index 000000000..a415f67a4 --- /dev/null +++ "b/spec/factories/users.rb\nusers.rb\nusers.rb" @@ -0,0 +1,11 @@ +FactoryGirl.define do + factory :user do + sequence :email do |n| + "person#{n}@example.com" + end + first_name "Foo" + last_name "Bar" + password "password" + password_confirmation "password" + end +end \ No newline at end of file diff --git a/spec/features/creating_a_new_post_spec.rb b/spec/features/creating_a_new_post_spec.rb new file mode 100644 index 000000000..4fb112b9d --- /dev/null +++ b/spec/features/creating_a_new_post_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper.rb' + +feature "A user can create a new post" do + let(:user) {create(:user)} + let(:user2) {create(:user, email: "blahblah2@gmail.com")} + + scenario "on their wall" do + login(user) + click_on("Timeline") + filled_in = "HELLLOO!!" + fill_in('post[content]', with: filled_in) + click_on("Post to the world!") + expect(page).to have_content(filled_in) + + end + + scenario "A user can create a post on someone else's wall" do + login(user) + visit "/users/#{user2.id}" + filled_in = "HELLLOO!!" + fill_in('post[content]', with: filled_in) + click_on("Post to the world!") + expect(page).to have_content(filled_in) + end + +end \ No newline at end of file diff --git a/spec/features/delete_a_post_spec.rb b/spec/features/delete_a_post_spec.rb new file mode 100644 index 000000000..f00be6d0f --- /dev/null +++ b/spec/features/delete_a_post_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper.rb' + +feature "Deleting a post" do + let(:user) {create(:user)} + let(:user2) {create(:user, email: "blah@gmail.com")} + let!(:post) {create(:post, user_id: user.id, content: "I AM A ROGUE POST", from: user2.id )} + + scenario "from a user's profile should delete it from their timeline and database" do + login(user) + click_on("Timeline") + expect(Post.count).to eq(1) + click_on("Delete Post") + expect(Post.count).to eq(0) + expect(page).to_not have_content("Delete Post") + end + + scenario "from a user not that current user should not change the post count" do + login(user2) + visit "/users/#{user.id}" + expect(Post.count).to eq(1) + expect(page).to_not have_content("Delete Post") + end + +end \ No newline at end of file diff --git a/spec/features/edit_profile_spec.rb b/spec/features/edit_profile_spec.rb new file mode 100644 index 000000000..6e44b246a --- /dev/null +++ b/spec/features/edit_profile_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper.rb' + +feature "Editing your profile" do + let(:user) {create(:user, email: "myemail@gmail.com")} + let(:user2) {create(:user, email: "blah@gmail.com")} + + scenario "a newly logged in user will be able to edit their profile page" do + login(user) + click_link("Edit") + fill_in('user_email', with:"mynewemail@gmail.com") + click_button("Submit") + + expect(page).to have_text("Your Danebook profile has been updated") + end + +end \ No newline at end of file diff --git a/spec/features/like_a_post_spec.rb b/spec/features/like_a_post_spec.rb new file mode 100644 index 000000000..b73afc2bc --- /dev/null +++ b/spec/features/like_a_post_spec.rb @@ -0,0 +1,36 @@ +require 'rails_helper.rb' + +feature "Liking a post" do + let(:user) {create(:user)} + let(:user2) {create(:user, email: "blah@gmail.com")} + let!(:post) {create(:post, user_id: user.id, content: "I AM A ROGUE POST", from: user2.id )} + + scenario "a user can like their own post" do + login(user) + click_on("Timeline") + expect(Like.count).to eq(0) + click_on("Like") + expect(Like.count).to eq(1) + end + + scenario "a user can like another person's post" do + login(user2) + visit "/users/#{user.id}" + expect(Like.count).to eq(0) + click_on("Like") + expect(Like.count).to eq(1) + end + + scenario "a user cannot like a post they have already liked" do + login(user) + click_on("Timeline") + expect(Like.count).to eq(0) + click_on("Like") + expect(Like.count).to eq(1) + click_on("Like") + expect(Like.count).to eq(1) + expect(page).to have_content("You already liked the comment +") + expect(page).to have_content("1") + end +end \ No newline at end of file diff --git a/spec/features/sign_in_spec.rb b/spec/features/sign_in_spec.rb new file mode 100644 index 000000000..c642144bc --- /dev/null +++ b/spec/features/sign_in_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper.rb' + + +feature "Signing in" do + let(:user) {create(:user)} + + scenario "a user who has an account will log in" do + login(user) + expect(page).to have_content("You've successfully signed into Danebook!") + end +end \ No newline at end of file diff --git a/spec/features/visit_homepage_spec.rb b/spec/features/visit_homepage_spec.rb new file mode 100644 index 000000000..a14dce165 --- /dev/null +++ b/spec/features/visit_homepage_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper.rb' + +feature "visiting homepage as a new user" do + + scenario "a new visitor will see a log in button" do + visit root_path + expect(page).to have_button("Log in") + end + + scenario "a new visitor will create a new Danebook account" do + visit root_path + fill_in("First Name", with: "foo") + fill_in("Last Name", with: "bar") + emails = page.all(".form-control") + page.all(:fillable_field, 'Email')[1].set("foo@bar.com") + page.all(:fillable_field, 'Password')[1].set("password") + fill_in("Password Confirmation", with: "password") + click_button("Sign Up!") + expect(page).to have_content("Welcome to Danebook!") + end + +end diff --git a/spec/features/visiting_timelines_spec.rb b/spec/features/visiting_timelines_spec.rb new file mode 100644 index 000000000..7e6a10285 --- /dev/null +++ b/spec/features/visiting_timelines_spec.rb @@ -0,0 +1,20 @@ +require 'rails_helper.rb' + +feature "visting timelines" do + let(:user) {create(:user)} + let(:user2) {create(:user, first_name: "Blah", email: "blah@gmail.com")} + + scenario "a signed in user should be able to visit any person's timeline" do + login(user) + visit "/users/#{user2.id}" + expect(page).to have_content(user2.first_name) + expect(page).to have_selector(".btn.btn-primary.center-block") + end + + scenario "a signed in user should be able to view their own timeline" do + login(user) + visit "/users/#{user.id}" + expect(page).to have_content(user.first_name) + expect(page).to have_selector(".btn.btn-primary.center-block") + end +end \ No newline at end of file diff --git a/spec/helpers/photos_helper_spec.rb b/spec/helpers/photos_helper_spec.rb new file mode 100644 index 000000000..6b5aa7d30 --- /dev/null +++ b/spec/helpers/photos_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the PhotosHelper. For example: +# +# describe PhotosHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe PhotosHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/helpers/relationships_helper_spec.rb b/spec/helpers/relationships_helper_spec.rb new file mode 100644 index 000000000..ff213d769 --- /dev/null +++ b/spec/helpers/relationships_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the RelationshipsHelper. For example: +# +# describe RelationshipsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe RelationshipsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb new file mode 100644 index 000000000..957e12b64 --- /dev/null +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -0,0 +1,4 @@ +# Preview all emails at http://localhost:3000/rails/mailers/user_mailer +class UserMailerPreview < ActionMailer::Preview + +end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb new file mode 100644 index 000000000..c48d3adee --- /dev/null +++ b/spec/mailers/user_mailer_spec.rb @@ -0,0 +1,5 @@ +require "rails_helper" + +RSpec.describe UserMailerMailer, type: :mailer do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb new file mode 100644 index 000000000..bc1505f49 --- /dev/null +++ b/spec/models/comment_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +describe Comment do + let(:comment_comment){create(:comment_comment)} + + describe "associations" do + let(:comment) {create(:comment)} + + it "belongs to itself" do + is_expected.to belong_to(:commentable) + end + + it "should return the commentable" do + expect(comment_comment).to respond_to(:commentable) + end + end + +end \ No newline at end of file diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb new file mode 100644 index 000000000..e69de29bb diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb new file mode 100644 index 000000000..7c7872d1b --- /dev/null +++ b/spec/models/photo_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +describe Photo do + + describe "Associations" do + + let(:photo) { create(:photo) } + + it "validates the presence of content" do + is_expected.to respond_to(:profile) + end + + end +end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb new file mode 100644 index 000000000..c6f2d0ba6 --- /dev/null +++ b/spec/models/post_spec.rb @@ -0,0 +1,68 @@ +require 'rails_helper' + +describe Post do + + describe "Validations" do + + let(:post) { create(:post) } + + it "validates the presence of content" do + is_expected.to validate_presence_of(:content) + end + + it "validates the length of the post" do + is_expected.to validate_length_of(:content).is_at_least(1) + end + + it "has a database column for user ids" do + is_expected.to have_db_column(:user_id) + end + + end + + describe "Associations" do + + let(:post) {create(:post)} + + it "accepts nested attributes for a comment" do + is_expected.to accept_nested_attributes_for(:comments) + end + + it "only has one user" do + is_expected.to belong_to(:user) + end + + it "has many comments" do + is_expected.to have_many(:comments) + end + + it "has many likes" do + is_expected.to have_many(:likes) + end + it "should return the comments" do + expect(post).to respond_to(:comments) + end + end + + describe "#all_comments" do + let(:post) {create(:post)} + + it "should list all comments in ascending order of a comment" do + 5.times do |n| + post.comments.build(created_at: Time.now) + end + post.comments.each_with_index do |comment, index| + expect(comment.created_at).to be < post.comments[index + 1].created_at if post.comments[index + 1] + end + end + + it "returns a list of all comments by the person" do + 10.times do |n| + post.comments.build(content: n) + end + all_comments = post.comments.length + expect(all_comments).to eq(10) + end + end + +end \ No newline at end of file diff --git a/spec/models/profile_spec.rb b/spec/models/profile_spec.rb new file mode 100644 index 000000000..ce0c905cf --- /dev/null +++ b/spec/models/profile_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +describe Profile do + + describe "associations" do + it "has one user" do + is_expected.to belong_to(:user) + end + end + +end \ No newline at end of file diff --git a/spec/models/relationship_spec.rb b/spec/models/relationship_spec.rb new file mode 100644 index 000000000..6e545f759 --- /dev/null +++ b/spec/models/relationship_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Relationship, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 000000000..be74d5ef6 --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,92 @@ +require 'rails_helper' + +describe User do + + describe "Validations" do + + let(:user) { build(:user) } + + it "validates the presence of first name" do + is_expected.to validate_presence_of(:first_name) + end + + it "validates the presence of a last name" do + is_expected.to validate_presence_of(:last_name) + end + + it "validates the presence of an email" do + is_expected.to validate_presence_of(:email) + end + + it "validates that the entered email is not taken already" do + is_expected.to validate_uniqueness_of(:email) + end + + it "should have a secure password" do + is_expected.to have_secure_password + end + + it "should validate a minimum and maximum length for password" do + should validate_length_of(:password).is_at_least(5).is_at_most(20) + end + + end + + describe "Associations" do + + it "should accept nested attribute for a profile object" do + is_expected.to accept_nested_attributes_for(:profile) + end + + it "should have many posts" do + is_expected.to have_many(:posts) + end + + it "should have many likes" do + is_expected.to have_many(:likes) + end + + it "should have one profile" do + is_expected.to have_one(:profile) + end + + end + + describe "#all_posts" do + let(:user) {build(:user)} + + it "should list all posts in ascending order of a user" do + 5.times do |n| + user.posts.build(created_at: Time.now, id: n) + end + user.posts.each_with_index do |post, index| + expect(post.created_at).to be < user.posts[index + 1].created_at if user.posts[index + 1] + end + end + end + + describe "#search" do + it "returns an array of all matching user objects with a given search input" do + bobs = Array.new(5) { |n| create(:user, first_name: ("Bob#{n}"), email: n )} + search_results = User.search("Bob") + bobs.each do |bob| + expect(search_results).to include(bob) + end + end + + it "should return an empty array of if nothing matches" do + bobs = Array.new(5) { |n| create(:user, first_name: ("Bob#{n}"), email: n )} + search_results = User.search("Joe") + expect(search_results).to be_empty + end + + it "should return unique users" do + bobs = Array.new(1) {create(:user, first_name:"Bob") } + bobs = bobs * 2 + search_results = User.search("Bob") + expect(search_results.count).to eq(1) + end + + end + +end \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 000000000..61c3aa8d7 --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,72 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'spec_helper' +require 'rspec/rails' +require 'factory_girl_rails' +require 'capybara/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } + +# Checks for pending migration and applies them before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.maintain_test_schema! + + +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + + config.include(LoginMacros) + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + config.include FactoryGirl::Syntax::Methods + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, :type => :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + # Choose a test framework: + with.test_framework :rspec + with.library :rails + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..8f698be46 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,99 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# The `.rspec` file also contains a few flags that are not defaults but that +# users commonly want. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/spec/support/login_macros.rb b/spec/support/login_macros.rb new file mode 100644 index 000000000..1c51b9185 --- /dev/null +++ b/spec/support/login_macros.rb @@ -0,0 +1,8 @@ +module LoginMacros + def login(user) + visit root_path + page.all(:fillable_field, 'Email')[0].set(user.email) + page.all(:fillable_field, 'Password')[0].set(user.password) + click_button("Log in") + end +end diff --git a/spec/support/signin_macros.rb b/spec/support/signin_macros.rb new file mode 100644 index 000000000..e69de29bb diff --git a/spec/views/users/index_spec.rb b/spec/views/users/index_spec.rb new file mode 100644 index 000000000..c4ce8519c --- /dev/null +++ b/spec/views/users/index_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe "users/index.html.erb" do + let(:user) {create(:user)} + let(:user2) {create(:user)} + + it "shows the search query" do + users = [user, user2] + assign(:users, users) + render + expect(rendered).to match(/#{user.first_name}/) + end + + it "does not show any user for an empty search query" do + users = [] + assign(:users, users) + render + expect(rendered).to_not match(/#{user.first_name}/) + end + +end diff --git a/spec/views/users/new_spec.rb b/spec/views/users/new_spec.rb new file mode 100644 index 000000000..a7eb0bde6 --- /dev/null +++ b/spec/views/users/new_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe "users/new.html.erb" do + let(:user){build(:user)} + let(:user2){create(:user)} + + context "signed in" do + before do + def view.signed_in_user? + true + end + @user = user2 + def view.current_user + @user + end + end + + it "will show welcome text" do + render :template => 'users/new', :layout => 'layouts/application' + expect(rendered).to have_content(/Welcome/) + end + end + +end \ No newline at end of file diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/assets/javascripts/.keep b/vendor/assets/javascripts/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/assets/stylesheets/.keep b/vendor/assets/stylesheets/.keep new file mode 100644 index 000000000..e69de29bb