diff --git a/.gitignore b/.gitignore index 4d898869..05878c95 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,13 @@ doc/ tests/coverage config.yml .DS_Store -public/assets/css/.sass-cache/ +public/assets/css/neo.css public/site_thumbnails public/sites public/site_screenshots +public/site_screenshots_test +public/site_thumbnails_test *.swp files/map.txt +.sass-cache +.sass-cache/* diff --git a/Gemfile b/Gemfile index 36634fb0..022c817b 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,6 @@ gem 'bcrypt' gem 'sinatra-flash', require: 'sinatra/flash' gem 'sinatra-xsendfile', require: 'sinatra/xsendfile' gem 'puma', require: nil -gem 'rubyzip', require: 'zip' gem 'rack-recaptcha', require: 'rack/recaptcha' gem 'rmagick', require: nil gem 'sidekiq' @@ -19,6 +18,10 @@ gem 'erubis' gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby' gem 'screencap' gem 'cocaine' +gem 'zipruby' +gem 'always_verify_ssl_certificates' +gem 'sass', require: nil +gem 'dav4rack' platform :mri do gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic @@ -45,7 +48,6 @@ end group :development do gem 'shotgun', require: nil - gem 'sass', require: nil end group :test do @@ -54,13 +56,13 @@ group :test do gem 'minitest' gem 'minitest-reporters', require: 'minitest/reporters' gem 'rack-test', require: 'rack/test' - gem 'webmock' gem 'mocha', require: nil gem 'rake', require: nil gem 'poltergeist' gem 'phantomjs', require: 'phantomjs/poltergeist' - gem 'capybara' gem 'capybara_minitest_spec' + gem 'rack_session_access', require: nil + gem 'webmock', require: nil platform :mri do gem 'simplecov', require: nil diff --git a/Gemfile.lock b/Gemfile.lock index e730f3ae..042aaf46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,6 +18,7 @@ GEM tzinfo (~> 1.1) addressable (2.3.6) ago (0.1.5) + always_verify_ssl_certificates (0.3.0) ansi (1.4.3) autoparse (0.3.3) addressable (>= 2.3.1) @@ -25,7 +26,7 @@ GEM multi_json (>= 1.0.0) bcrypt (3.1.7) builder (3.2.2) - capybara (2.2.1) + capybara (2.4.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -42,16 +43,20 @@ GEM cocaine (0.5.4) climate_control (>= 0.0.3, < 1.0) coderay (1.1.0) - columnize (0.3.6) + columnize (0.8.9) connection_pool (2.0.0) crack (0.4.2) safe_yaml (~> 1.0.0) + dav4rack (0.3.0) + nokogiri (>= 1.4.2) + rack (>= 1.1.0) + uuidtools (~> 2.1.1) debugger (1.6.6) columnize (>= 0.3.1) debugger-linecache (~> 1.2.0) debugger-ruby_core_source (~> 1.3.2) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.3.2) + debugger-ruby_core_source (1.3.5) docile (1.1.3) erubis (2.7.0) extlib (0.9.16) @@ -89,7 +94,7 @@ GEM metaclass (0.0.4) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.5.3) + mini_portile (0.6.0) minitest (5.3.1) minitest-reporters (1.0.2) ansi @@ -98,13 +103,13 @@ GEM powerbar mocha (1.0.0) metaclass (~> 0.0.1) - multi_json (1.9.2) + multi_json (1.10.1) multipart-post (2.0.0) - nokogiri (1.6.1) - mini_portile (~> 0.5.0) + nokogiri (1.6.3.1) + mini_portile (= 0.6.0) pg (0.17.1) phantomjs (1.9.7.0) - poltergeist (1.5.0) + poltergeist (1.5.1) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -129,6 +134,9 @@ GEM json rack-test (0.6.2) rack (>= 1.0) + rack_session_access (0.1.1) + builder (>= 2.0.0) + rack (>= 1.0.0) rainbows (4.6.1) kgio (~> 2.5) rack (~> 1.1) @@ -142,7 +150,6 @@ GEM mime-types (>= 1.16) retriable (1.4.1) rmagick (2.13.2) - rubyzip (1.1.2) safe_yaml (1.0.1) sass (3.3.8) screencap (0.1.1) @@ -194,19 +201,21 @@ GEM webmock (1.17.4) addressable (>= 2.2.7) crack (>= 0.3.2) - websocket-driver (0.3.2) + websocket-driver (0.3.4) xpath (2.0.0) nokogiri (~> 1.3) + zipruby (0.3.6) PLATFORMS ruby DEPENDENCIES ago + always_verify_ssl_certificates bcrypt - capybara capybara_minitest_spec cocaine + dav4rack erubis fabrication faker @@ -228,12 +237,12 @@ DEPENDENCIES puma rack-recaptcha rack-test + rack_session_access rainbows rake redis rmagick ruby-debug - rubyzip sass screencap sequel (= 4.8.0) @@ -247,3 +256,4 @@ DEPENDENCIES stripe! tilt webmock + zipruby diff --git a/Rakefile b/Rakefile index 9e80f27c..1fcc1216 100644 --- a/Rakefile +++ b/Rakefile @@ -7,7 +7,7 @@ end desc "Run all tests" Rake::TestTask.new do |t| t.libs << "spec" - t.test_files = FileList['tests/*_tests.rb'] + t.test_files = FileList['tests/**/*_tests.rb'] t.verbose = true end @@ -87,4 +87,4 @@ end desc 'Compile domain map for nginx' task :compile_domain_map => [:environment] do File.open('./files/map.txt', 'w'){|f| Site.exclude(domain: nil).exclude(domain: '').select(:username,:domain).all.collect {|s| f.write "#{s.domain} #{s.username};\n" }} -end \ No newline at end of file +end diff --git a/app.rb b/app.rb index 1774496b..77e9151e 100644 --- a/app.rb +++ b/app.rb @@ -452,6 +452,17 @@ end get '/dashboard' do require_login + + if params[:dir] && params[:dir][0] != '/' + params[:dir] = '/'+params[:dir] + end + + if !File.directory?(current_site.files_path(params[:dir])) + redirect '/dashboard' + end + + @dir = params[:dir] + @file_list = current_site.file_list @dir erb :'dashboard' end @@ -562,7 +573,7 @@ post '/change_name' do end old_host = current_site.host - old_file_paths = current_site.file_list.collect {|f| f.filename} + old_file_paths = current_site.file_list.collect {|f| f[:path]} current_site.username = params[:name] @@ -629,7 +640,8 @@ def file_upload_response(error=nil) @error = error halt 200, erb(:'dashboard') else - redirect '/dashboard' + query_string = params[:dir] ? "?"+Rack::Utils.build_query(dir: params[:dir]) : '' + redirect "/dashboard#{query_string}" end else halt http_error_code, error if error @@ -637,6 +649,20 @@ def file_upload_response(error=nil) end end +post '/site/create_directory' do + require_login + + path = "#{params[:dir] || ''}/#{params[:name]}" + + result = current_site.create_directory path + + if result != true + flash[:error] = e.message + end + + redirect "/dashboard?dir=#{Rack::Utils.escape params[:dir]}" +end + post '/site_files/upload' do require_login @errors = [] @@ -647,12 +673,12 @@ post '/site_files/upload' do end params[:files].each do |file| + file[:filename] = "#{params[:dir]}/#{file[:filename]}" if params[:dir] if current_site.file_size_too_large? file[:tempfile].size - file_upload_response "#{file[:filename]} is too large, upload cancelled." + file_upload_response "#{params[:dir]}/#{file[:filename]} is too large, upload cancelled." end - if !Site.valid_file_type? file - file_upload_response "#{file[:filename]}: file type (or content in file) is not allowed on Neocities, upload cancelled." + file_upload_response "#{params[:dir]}/#{file[:filename]}: file type (or content in file) is not allowed on Neocities, upload cancelled." end end @@ -664,9 +690,8 @@ post '/site_files/upload' do results = [] params[:files].each do |file| - results << current_site.store_file(Site.sanitize_filename(file[:filename]), file[:tempfile]) + results << current_site.store_file(file[:filename], file[:tempfile]) end - current_site.increment_changed_count if results.include?(true) file_upload_response @@ -674,20 +699,18 @@ end post '/site_files/delete' do require_login - sanitized_filename = Site.sanitize_filename params[:filename] + current_site.delete_file params[:filename] - current_site.delete_file(sanitized_filename) - - flash[:success] = "Deleted file #{params[:filename]}." + flash[:success] = "Deleted #{params[:filename]}." redirect '/dashboard' end get '/site_files/:username.zip' do |username| require_login - zipfile = current_site.files_zip + zipfile_path = current_site.files_zip content_type 'application/octet-stream' - attachment "#{current_site.username}.zip" - zipfile + attachment "neocities-#{current_site.username}.zip" + send_file zipfile_path end get '/site_files/download/:filename' do |filename| @@ -697,10 +720,11 @@ get '/site_files/download/:filename' do |filename| current_site.get_file filename end -get '/site_files/text_editor/:filename' do |filename| +get %r{\/site_files\/text_editor\/(.+)} do require_login + @filename = params[:captures].first begin - @file_data = current_site.get_file filename + @file_data = current_site.get_file @filename rescue Errno::ENOENT flash[:error] = 'We could not find the requested file.' redirect '/dashboard' @@ -708,8 +732,9 @@ get '/site_files/text_editor/:filename' do |filename| erb :'site_files/text_editor' end -post '/site_files/save/:filename' do |filename| +post %r{\/site_files\/save\/(.+)} do require_login_ajax + filename = params[:captures].first tempfile = Tempfile.new 'neocities_saving_file' @@ -722,9 +747,7 @@ post '/site_files/save/:filename' do |filename| halt 'File is too large to fit in your space, it has NOT been saved. Please make a local copy and then try to reduce the size.' end - sanitized_filename = Site.sanitize_filename filename - - current_site.store_file sanitized_filename, tempfile + current_site.store_file filename, tempfile 'ok' end @@ -937,14 +960,11 @@ end post '/api/upload' do require_api_credentials - files = [] - params.each do |k,v| next unless v.is_a?(Hash) && v[:tempfile] - filename = k.to_s - api_error(400, 'bad_filename', "#{filename} is not a valid filename, files not uploaded") unless Site.valid_filename? filename - files << {filename: filename, tempfile: v[:tempfile]} + path = k.to_s + files << {filename: k || v[:filename], tempfile: v[:tempfile]} end api_error 400, 'missing_files', 'you must provide files to upload' if files.empty? @@ -959,6 +979,10 @@ post '/api/upload' do if !Site.valid_file_type?(file) api_error 400, 'invalid_file_type', "#{file[:filename]} is not a valid file type (or contains not allowed content), files have not been uploaded" end + + if File.directory? file[:filename] + api_error 400, 'directory_exists', 'this name is being used by a directory, cannot continue' + end end results = [] @@ -976,26 +1000,25 @@ post '/api/delete' do api_error 400, 'missing_filenames', 'you must provide files to delete' if params[:filenames].nil? || params[:filenames].empty? - filenames = [] - - params[:filenames].each do |filename| - unless filename.is_a?(String) && Site.valid_filename?(filename) - api_error 400, 'bad_filename', "#{filename} is not a valid filename, canceled deleting" + paths = [] + params[:filenames].each do |path| + unless path.is_a?(String) + api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" end - if !current_site.file_exists?(filename) - api_error 400, 'missing_files', "#{filename} was not found on your site, canceled deleting" + if !current_site.file_exists?(path) + api_error 400, 'missing_files', "#{path} was not found on your site, canceled deleting" end - if filename == 'index.html' + if path == 'index.html' api_error 400, 'cannot_delete_index', 'you cannot delete your index.html file, canceled deleting' end - filenames << filename + paths << path end - filenames.each do |filename| - current_site.delete_file(filename) + paths.each do |path| + current_site.delete_file(path) end api_success 'file(s) have been deleted' diff --git a/config.ru b/config.ru index c831afe5..128f127c 100644 --- a/config.ru +++ b/config.ru @@ -1,8 +1,64 @@ +require 'rubygems' require './app.rb' require 'sidekiq/web' map('/') { run Sinatra::Application } +map '/webdav' do + + use Rack::Auth::Basic do |username, password| + Site.valid_login? username, password + end + + run lambda {|env| + site = Site[username: env['REMOTE_USER']] + + if env['REQUEST_METHOD'] == 'PUT' + path = env['PATH_INFO'] + tmpfile = Tempfile.new 'davfile', encoding: 'binary' + tmpfile.write env['rack.input'].read + tmpfile.close + + if site.file_size_too_large? tmpfile.size + return [507, {}, ['']] + end + + if Site.valid_file_type?(filename: path, tempfile: tmpfile) + site.store_file path, tmpfile + return [201, {}, ['']] + else + return [415, {}, ['']] + end + end + + if env['REQUEST_METHOD'] == 'MOVE' + tmpfile = Tempfile.new 'moved_file' + tmpfile.close + + destination = env['HTTP_DESTINATION'].match(/^.+\/webdav(.+)$/i).captures.first + + FileUtils.cp site.files_path(env['PATH_INFO']), tmpfile.path + + DB.transaction do + site.store_file destination, tmpfile + site.delete_file env['PATH_INFO'] + end + + return [201, {}, ['']] + end + + if env['REQUEST_METHOD'] == 'DELETE' + site.delete_file env['PATH_INFO'] + return [201, {}, ['']] + end + + res = DAV4Rack::Handler.new( + root: Site.select(:username).where(username: env['REMOTE_USER']).first.files_path, + root_uri_path: '/webdav' + ).call(env) + } +end + map '/sidekiq' do use Rack::Auth::Basic, "Protected Area" do |username, password| raise 'missing sidekiq auth' unless $config['sidekiq_user'] && $config['sidekiq_pass'] @@ -10,4 +66,4 @@ map '/sidekiq' do end run Sidekiq::Web -end +end \ No newline at end of file diff --git a/environment.rb b/environment.rb index 10385a8f..bebfe8d1 100644 --- a/environment.rb +++ b/environment.rb @@ -7,7 +7,6 @@ Encoding.default_external = 'UTF-8' require 'yaml' require 'json' require 'logger' -require 'zip' Bundler.require Bundler.require :development if ENV['RACK_ENV'] == 'development' @@ -28,8 +27,6 @@ end DB = Sequel.connect $config['database'], sslmode: 'disable', max_connections: $config['database_pool'] DB.extension :pagination -Dir.glob('workers/*.rb').each {|w| require File.join(DIR_ROOT, "/#{w}") } - if defined?(Pry) Pry.commands.alias_command 'c', 'continue' Pry.commands.alias_command 's', 'step' @@ -60,9 +57,9 @@ if $config['pubsub_url'].nil? && ENV['RACK_ENV'] == 'production' raise 'pubsub_url is missing from config' end -require File.join(DIR_ROOT, 'workers', 'thumbnail_worker.rb') -require File.join(DIR_ROOT, 'workers', 'screenshot_worker.rb') -require File.join(DIR_ROOT, 'workers', 'email_worker.rb') +#require File.join(DIR_ROOT, 'workers', 'thumbnail_worker.rb') +#require File.join(DIR_ROOT, 'workers', 'screenshot_worker.rb') +#require File.join(DIR_ROOT, 'workers', 'email_worker.rb') Sequel.datetime_class = Time Sequel.extension :core_extensions @@ -77,6 +74,9 @@ Sequel::Migrator.apply DB, './migrations' Stripe.api_key = $config['stripe_api_key'] Dir.glob('models/*.rb').each {|m| require File.join(DIR_ROOT, "#{m}") } +Dir.glob('workers/*.rb').each {|w| require File.join(DIR_ROOT, "/#{w}") } + + #DB.loggers << Logger.new(STDOUT) if ENV['RACK_ENV'] == 'development' if ENV['RACK_ENV'] == 'development' @@ -100,3 +100,16 @@ Mail.defaults do end Sinatra::Application.set :erb, escape_html: true + +require 'sass/plugin/rack' +Sinatra::Application.use Sass::Plugin::Rack + +Sass::Plugin.options[:template_location] = './public/assets/css' +Sass::Plugin.options[:css_location] = './public/assets/css' +Sass::Plugin.options[:style] = :nested + +if ENV['RACK_ENV'] == 'production' + Sass::Plugin.options[:style] = :compressed + Sass::Plugin.options[:never_update] = true + Sass::Plugin.options[:full_exception] = false +end \ No newline at end of file diff --git a/models/site.rb b/models/site.rb index 11e3e0f2..c93d258b 100644 --- a/models/site.rb +++ b/models/site.rb @@ -1,6 +1,7 @@ require 'tilt' require 'rss' require 'nokogiri' +require 'pathname' class Site < Sequel::Model include Sequel::ParanoidDelete @@ -23,6 +24,7 @@ class Site < Sequel::Model image/x-icon application/pdf application/pgp-keys + application/pgp text/xml application/xml audio/midi @@ -49,8 +51,8 @@ class Site < Sequel::Model SITE_FILES_ROOT = File.join PUBLIC_ROOT, (ENV['RACK_ENV'] == 'test' ? 'sites_test' : 'sites') SCREENSHOTS_ROOT = File.join(PUBLIC_ROOT, (ENV['RACK_ENV'] == 'test' ? 'site_screenshots_test' : 'site_screenshots')) THUMBNAILS_ROOT = File.join(PUBLIC_ROOT, (ENV['RACK_ENV'] == 'test' ? 'site_thumbnails_test' : 'site_thumbnails')) - SCREENSHOTS_URL_ROOT = '/site_screenshots' - THUMBNAILS_URL_ROOT = '/site_thumbnails' + SCREENSHOTS_URL_ROOT = ENV['RACK_ENV'] == 'test' ? '/site_screenshots_test' : '/site_screenshots' + THUMBNAILS_URL_ROOT = ENV['RACK_ENV'] == 'test' ? '/site_thumbnails_test' : '/site_thumbnails' IMAGE_REGEX = /jpg|jpeg|png|bmp|gif/ LOSSLESS_IMAGE_REGEX = /png|bmp|gif/ LOSSY_IMAGE_REGEX = /jpg|jpeg/ @@ -70,6 +72,8 @@ class Site < Sequel::Model EMAIL_SANITY_REGEX = /.+@.+\..+/i + EDITABLE_FILE_EXT = /html|htm|txt|js|css|md/i + BANNED_TIME = 2592000 # 30 days in seconds TITLE_MAX = 100 @@ -197,16 +201,16 @@ class Site < Sequel::Model FileUtils.mkdir_p files_path %w{index not_found}.each do |name| - File.write file_path("#{name}.html"), render_template("#{name}.erb") - purge_cache "#{name}.html" + File.write files_path("#{name}.html"), render_template("#{name}.erb") + purge_cache "/#{name}.html" ScreenshotWorker.perform_async values[:username], "#{name}.html" end - FileUtils.cp template_file_path('cat.png'), file_path('cat.png') + FileUtils.cp template_file_path('cat.png'), files_path('cat.png') end - def get_file(filename) - File.read file_path(filename) + def get_file(path) + File.read files_path(path) end def before_destroy @@ -252,8 +256,8 @@ class Site < Sequel::Model FileUtils.mv files_path, File.join(PUBLIC_ROOT, 'banned_sites', username) } - site_files.file_list.collect {|f| f.filename}.each do |f| - purge_cache f + file_list.each do |path| + purge_cache path end end @@ -290,15 +294,6 @@ class Site < Sequel::Model !@blockings.select {|b| b.site_id == site.id}.empty? end - def self.valid_filename?(filename) - return false if sanitize_filename(filename) != filename - true - end - - def self.sanitize_filename(filename) - filename.gsub(/[^a-zA-Z0-9_\-.]/, '') - end - def self.valid_username?(username) !username.empty? && username.match(/^[a-zA-Z0-9_\-]+$/i) end @@ -331,19 +326,24 @@ class Site < Sequel::Model true end - def purge_cache(filename) - payload = {site: username, path: filename} + def purge_cache(path) + relative_path = path.gsub(base_files_path, '') + payload = {site: username, path: relative_path} payload[:domain] = domain if !domain.empty? PurgeCacheWorker.perform_async payload end - def store_file(filename, uploaded) - if File.exist?(file_path(filename)) && - Digest::SHA2.file(file_path(filename)).digest == Digest::SHA2.file(uploaded.path).digest + def store_file(path, uploaded) + relative_path = scrubbed_path path + path = files_path path + + if File.exist?(path) && + Digest::SHA2.file(path).digest == Digest::SHA2.file(uploaded.path).digest return false end - if filename == 'index.html' + pathname = Pathname(path) + if pathname.basename.to_s == 'index.html' new_title = Nokogiri::HTML(File.read(uploaded.path)).css('title').first.text if new_title.length < TITLE_MAX @@ -352,20 +352,26 @@ class Site < Sequel::Model end end - FileUtils.mv uploaded.path, file_path(filename) - File.chmod(0640, file_path(filename)) + dirname = pathname.dirname.to_s - purge_cache filename - - ext = File.extname(filename).gsub(/^./, '') - - if ext.match HTML_REGEX - ScreenshotWorker.perform_async values[:username], filename - elsif ext.match IMAGE_REGEX - ThumbnailWorker.perform_async values[:username], filename + if !File.exists? dirname + FileUtils.mkdir_p dirname end - SiteChange.record self, filename + FileUtils.mv uploaded.path, path + File.chmod 0640, path + + purge_cache path + + ext = File.extname(path).gsub(/^./, '') + + if ext.match HTML_REGEX + ScreenshotWorker.perform_async values[:username], relative_path + elsif ext.match IMAGE_REGEX + ThumbnailWorker.perform_async values[:username], relative_path + end + + SiteChange.record self, relative_path if self.site_changed != true self.site_changed = true @@ -375,6 +381,20 @@ class Site < Sequel::Model true end + def is_directory?(path) + File.directory? files_path(path) + end + + def create_directory(path) + relative_path = files_path path + if Dir.exists?(relative_path) || File.exist?(relative_path) + return 'Directory (or file) already exists.' + end + + FileUtils.mkdir_p relative_path + true + end + def increment_changed_count self.changed_count += 1 self.updated_at = Time.now @@ -382,49 +402,61 @@ class Site < Sequel::Model end def files_zip - file_path = "/tmp/neocities-site-#{username}.zip" + zip_name = "neocities-#{username}" - Zip::File.open(file_path, Zip::File::CREATE) do |zipfile| - file_list.collect {|f| f.filename}.each do |filename| - zipfile.add filename, file_path(filename) + tmpfile = Tempfile.new 'neocities-site-zip' + tmpfile.close + + Zip::Archive.open(tmpfile.path, Zip::CREATE) do |ar| + ar.add_dir(zip_name) + + Dir.glob("#{base_files_path}/**/*").each do |path| + relative_path = path.gsub(base_files_path+'/', '') + + if File.directory?(path) + ar.add_dir(zip_name+'/'+relative_path) + else + ar.add_file(zip_name+'/'+relative_path, path) # add_file(, ) + end end end - # TODO Don't dump the zipfile into memory - zipfile = File.read file_path - File.delete file_path - zipfile + tmpfile.path end - def delete_file(filename) + def delete_file(path) begin - FileUtils.rm file_path(filename) + FileUtils.rm files_path(path) + rescue Errno::EISDIR + FileUtils.remove_dir files_path(path), true rescue Errno::ENOENT end - purge_cache filename + purge_cache path - ext = File.extname(filename).gsub(/^./, '') + ext = File.extname(path).gsub(/^./, '') - screenshots_delete(filename) if ext.match HTML_REGEX - thumbnails_delete(filename) if ext.match IMAGE_REGEX + screenshots_delete(path) if ext.match HTML_REGEX + thumbnails_delete(path) if ext.match IMAGE_REGEX - SiteChangeFile.filter(site_id: self.id, filename: filename).delete + path = path[1..path.length] if path[0] == '/' + + SiteChangeFile.filter(site_id: self.id, filename: path).delete true end def move_files_from(oldusername) - FileUtils.mv files_path(oldusername), files_path + FileUtils.mv base_files_path(oldusername), base_files_path end - def install_new_html_file(name) - File.write file_path(name), render_template('index.erb') - purge_cache name + def install_new_html_file(path) + File.write files_path(path), render_template('index.erb') + purge_cache path end - def file_exists?(filename) - File.exist? file_path(filename) + def file_exists?(path) + File.exist? files_path(path) end def after_save @@ -453,7 +485,7 @@ class Site < Sequel::Model end # def after_destroy -# FileUtils.rm_rf file_path +# FileUtils.rm_rf files_path # super # end @@ -568,16 +600,48 @@ class Site < Sequel::Model File.join TEMPLATE_ROOT, name end - def files_path(name=nil) - File.join SITE_FILES_ROOT, (name || username) + def base_files_path(name=username) + raise 'username missing' if name.nil? || name.empty? + File.join SITE_FILES_ROOT, name end - def file_path(filename) - File.join files_path, filename + # https://practicingruby.com/articles/implementing-an-http-file-server?u=dc2ab0f9bb + def scrubbed_path(path='') + path ||= '' + clean = [] + + parts = path.split '/' + + parts.each do |part| + next if part.empty? || part == '.' + clean << part if part != '..' + end + + clean.join '/' end - def file_list - Dir.glob(File.join(files_path, '*')).collect {|p| File.basename(p)}.sort.collect {|sitename| SiteFile.new sitename} + def files_path(path='') + File.join base_files_path, scrubbed_path(path) + end + + def file_list(path='') + list = Dir.glob(File.join(files_path(path), '*')).collect do |file_path| + file = { + path: file_path.gsub(base_files_path, ''), + name: File.basename(file_path), + ext: File.extname(file_path).gsub('.', ''), + is_directory: File.directory?(file_path), + is_root_index: file_path == "#{base_files_path}/index.html" + } + + file[:is_html] = !(file[:ext].match HTML_REGEX).nil? + file[:is_image] = !(file[:ext].match IMAGE_REGEX).nil? + file[:is_editable] = !(file[:ext].match EDITABLE_FILE_EXT).nil? + file + end + + list.select {|f| f[:is_directory]}.sort_by {|f| f[:name]} + + list.select {|f| f[:is_directory] == false}.sort_by{|f| f[:name]} end def file_size_too_large?(size_in_bytes) @@ -658,19 +722,19 @@ class Site < Sequel::Model values[:hits].to_s.reverse.gsub(/...(?=.)/,'\&,').reverse end - def screenshots_delete(filename) + def screenshots_delete(path) SCREENSHOT_RESOLUTIONS.each do |res| begin - FileUtils.rm screenshot_path(filename, res) + FileUtils.rm screenshot_path(path, res) rescue Errno::ENOENT end end end - def thumbnails_delete(filename) + def thumbnails_delete(path) THUMBNAIL_RESOLUTIONS.each do |res| begin - FileUtils.rm thumbnail_path(filename, res) + FileUtils.rm thumbnail_path(path, res) rescue Errno::ENOENT end end @@ -680,34 +744,34 @@ class Site < Sequel::Model Site.where(tags: tags).limit(limit, offset).order(:updated_at.desc).all end - def screenshot_path(filename, resolution) - File.join(SCREENSHOTS_ROOT, values[:username], "#{filename}.#{resolution}.jpg") + def screenshot_path(path, resolution) + File.join(SCREENSHOTS_ROOT, values[:username], "#{path}.#{resolution}.jpg") end - def screenshot_exists?(filename, resolution) - File.exist? File.join(SCREENSHOTS_ROOT, values[:username], "#{filename}.#{resolution}.jpg") + def screenshot_exists?(path, resolution) + File.exist? File.join(SCREENSHOTS_ROOT, values[:username], "#{path}.#{resolution}.jpg") end - def screenshot_url(filename, resolution) - "#{SCREENSHOTS_URL_ROOT}/#{values[:username]}/#{filename}.#{resolution}.jpg" + def screenshot_url(path, resolution) + "#{SCREENSHOTS_URL_ROOT}/#{values[:username]}/#{path}.#{resolution}.jpg" end - def thumbnail_path(filename, resolution) - ext = File.extname(filename).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - File.join THUMBNAILS_ROOT, values[:username], "#{filename}.#{resolution}.#{ext}" + def thumbnail_path(path, resolution) + ext = File.extname(path).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' + File.join THUMBNAILS_ROOT, values[:username], "#{path}.#{resolution}.#{ext}" end - def thumbnail_exists?(filename, resolution) - File.exist? thumbnail_path(filename, resolution) + def thumbnail_exists?(path, resolution) + File.exist? thumbnail_path(path, resolution) end - def thumbnail_delete(filename, resolution) - File.rm thumbnail_path(filename, resolution) + def thumbnail_delete(path, resolution) + File.rm thumbnail_path(path, resolution) end - def thumbnail_url(filename, resolution) - ext = File.extname(filename).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - "#{THUMBNAILS_URL_ROOT}/#{values[:username]}/#{filename}.#{resolution}.#{ext}" + def thumbnail_url(path, resolution) + ext = File.extname(path).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' + "#{THUMBNAILS_URL_ROOT}/#{values[:username]}/#{path}.#{resolution}.#{ext}" end def to_rss diff --git a/models/site_file.rb b/models/site_file.rb deleted file mode 100644 index ac280f17..00000000 --- a/models/site_file.rb +++ /dev/null @@ -1,8 +0,0 @@ -class SiteFile - attr_reader :filename, :ext - - def initialize(filename) - @filename = filename - @ext = File.extname(@filename).sub(/^./, '') - end -end \ No newline at end of file diff --git a/public/assets/css/.gitignore b/public/assets/css/.gitignore deleted file mode 100644 index af3f6036..00000000 --- a/public/assets/css/.gitignore +++ /dev/null @@ -1 +0,0 @@ -# This file is here simplt to force git to allow the folder within the zip \ No newline at end of file diff --git a/public/assets/css/_project-sass/_project-Main.scss b/public/assets/css/_project-sass/_project-Main.scss index 40c94a67..5bb743ac 100644 --- a/public/assets/css/_project-sass/_project-Main.scss +++ b/public/assets/css/_project-sass/_project-Main.scss @@ -253,7 +253,7 @@ } .file .title { font-weight: bold; - font-size: 12px; + font-size: 8px; color: #666; margin-top: 4px; text-decoration: none; diff --git a/public/assets/css/neo.css b/public/assets/css/neo.css deleted file mode 100644 index 748ba3dd..00000000 --- a/public/assets/css/neo.css +++ /dev/null @@ -1,2245 +0,0 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,700,700italic,600,400italic,300italic,600italic); -@import url(//fonts.googleapis.com/css?family=Droid+Serif:400,700); -*, *:before, *:after { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -html { - overflow-y: scroll; - min-height: 100%; } - -body { - line-height: 1.5; } - -body, blockquote, h1, h2, h3, h4, h5, h6, p, pre, form, fieldset, img, -legend, table, th, td, caption, hr { - border: 0; - margin: 0; - outline: 0; - padding: 0; } - -hr { - border-top: 1px solid #ddd; - display: block; - height: 1px; - margin: 24px 0; } - -article, aside, details, figure, figcaption, footer, -header, main, nav, section, summary { - display: block; } - -[hidden] { - display: none; } - -img { - color: red; - font-style: italic; } - -audio, img, object, embed, video { - max-width: 100%; } - -audio, canvas, video { - display: inline-block; } - -audio:not([controls]) { - display: none; - height: 0; } - -svg:not(:root) { - overflow: hidden; } - -small { - display: block; } - -p small, li small { - display: inline; - margin: 0; } - -b, strong { - font-weight: bold; } - -i, em, dfn { - font-style: italic; } - -blockquote { - font-size: 1.125em; - font-style: italic; } - blockquote:before, blockquote:after { - font-size: 1.375em; - font-weight: 400; - line-height: 1; - position: relative; - top: 2px; } - blockquote:before { - content: '"'; - left: -1px; } - blockquote:after { - content: '"'; - right: -1px; } - -q { - quotes: "\201C" "\201D" "\2018" "\2019"; } - -abbr[title], dfn[title] { - border-bottom: 1px dotted #cccccc; - cursor: help; } - -mark { - background: yellow; - color: #131313; } - -sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; } - -sub { - bottom: -.25em; } - -sup { - top: -.5em; } - -code, kbd, pre, samp { - background: #16414c; - border-left: 6px solid #acd473; - color: #a9b9b9; - display: block; - font-family: monospace, serif; - font-size: 0.9em; - padding: 12px 8px 12px 20px; } - -pre { - overflow: auto; - white-space: pre-wrap; } - pre mark { - background: #eee; - border-bottom: 1px solid #ddd; - color: #333; } - -p code, p kbd, p pre, p samp, li code, li kbd, li pre, li samp, pre code, pre kbd, pre pre, pre samp { - display: inline-block; - margin: 0; - padding: 4px; } - -.code-Value { - color: #24b9af; } - -.code-Tag { - color: #8ab04c; } - -ol, ul { - margin: 0 0 8px; - padding: 0 0 0 24px; } - ol > li:last-child, ul > li:last-child { - margin-bottom: 0; } - ol li ol, ol li ul, ul li ol, ul li ul { - margin-top: 8px; } - ol li li, ul li li { - font-size: 1em; } - -dd { - margin: 0 0 8px; - padding-left: 16px; } - -button, input, select, textarea { - border: 0; - font-family: inherit; - font-size: 100%; - line-height: normal; - margin: 0; - text-transform: none; } - -button, html input[type='button'], input[type='reset'], input[type='submit'] { - cursor: pointer; - -webkit-appearance: button; } - -input[type='search'] { - -webkit-appearance: textfield; } - -input[disabled] { - background: #eeeeee; - cursor: not-allowed; } - -input[readonly] { - background: #fafafa; } - -input[type='search']::-webkit-search-decoration { - -webkit-appearance: none; } - -buton::-moz-focus-inner, input::-moz-focus-inner { - border: 0; - padding: 0; } - -textarea { - overflow: auto; - vertical-align: top; - border: 2px solid #DCE4EC; } - -.tooltip-inner { - white-space: pre-wrap; } - -table { - border-collapse: collapse; - border-spacing: 0; } - -.table-Base, .table-Border, .table-Stripe { - background: #fafafa; - font-size: 0.9em; - width: 100%; } - .table-Base th, .table-Border th, .table-Stripe th, - .table-Base td, - .table-Border td, - .table-Stripe td { - padding: 4px; - text-align: left; } - -.table-Header { - background: #eee; } - -.table-Footer { - background: #e3e3e3; } - -.table-Border { - border-bottom: 1px solid #cccccc; - border-right: 1px solid #cccccc; } - .table-Border tr { - border-top: 1px solid #cccccc; } - .table-Border th, .table-Border td { - border-left: 1px solid #cccccc; } - -.table-Stripe tr:nth-child(2n) { - background: #eeeeee; } - -.row, .c-Row { - margin-left: -20px; - margin-bottom: 20px; } - -.col { - float: left; - margin-bottom: 0 !important; - padding-left: 20px; - position: relative; - width: 100%; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .col { - float: none; - padding: 0; } } - -.c-Row { - font-size: 0; - text-align: center; } - .c-Row .col { - display: inline-block; - float: none; - font-size: 16px; - text-align: left; } - -.col-90 { - width: 90%; } - -.col-80 { - width: 80%; } - -.col-75 { - width: 75%; } - -.col-66 { - width: 66.6666%; } - -.col-60 { - width: 60%; } - -.col-50 { - width: 50%; } - -.col-40 { - width: 40%; } - -.col-33 { - width: 33.3333%; } - -.col-25, .website-Gallery li { - width: 25%; } - -.col-20 { - width: 20%; } - -.col-10 { - width: 10%; } - -.rfl { - padding-left: 300px; } - .rfl .f-Col { - float: left; - margin-left: -280px; - width: 280px; } - -.rfr { - padding-right: 300px; } - .rfr .f-Col { - float: right; - margin-right: -300px; - width: 280px; } - -.block { - background: #ccc; - color: #333; - padding: 4px; } - .block > :last-child { - margin-bottom: 0; } - -nav ul, -nav ol { - list-style: none; - margin: 0; - padding: 0; } -nav li { - margin: 0; } -nav a { - display: inline-block; - padding: 4px 8px; - text-decoration: underline; } - nav a:hover { - text-decoration: none; } - -.grouping { - padding: 4px 0; } - -.fs-Legend { - border: 1px solid #cccccc; - margin-bottom: 8px; - padding: 8px 12px; } - -legend, .legend { - font-size: 1.375em; - margin-left: -4px; - padding: 0 4px; } - -/* Text Input Areas & Labels */ -.text-Label, .option-Container { - font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; - cursor: pointer; - display: block; - margin-bottom: 4px; } - -.dis-Label { - cursor: not-allowed; } - -.input-Area, .text-Area, .select-Container, .input-Number { - background: #fff; - border: 2px solid #cccccc; - font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; - line-height: 1.25; - margin-bottom: 8px; - padding: 8px 4px; - width: 80%; } - .input-Area:focus, .text-Area:focus, .select-Container:focus, .input-Number:focus { - background: #f8f8f8; - border: 2px solid #50B6D5; - -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; - -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; - box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; } - -.text-Area { - display: block; - min-height: 150px; - resize: vertical; - width: 100%; } - -/* Check/Radio Inputs & Labels */ -.option-Container { - position: relative; } - .option-Container:hover .btn-Radio, - .option-Container:hover .btn-Check { - border-color: #333; } - -.option-Label { - cursor: pointer; - display: block; - padding-left: 28px; - position: relative; - z-index: 2; } - -.input-Hide { - border: 0 !important; - height: 1px !important; - left: -999999px !important; - overflow: hidden !important; - position: absolute !important; - width: 1px !important; } - -.btn-Radio, .btn-Check { - background: #eeeeee; - border: 1px solid #ccc; - font-size: 14px; - font-weight: bold; - height: 19px; - left: 0; - padding: 2px; - position: absolute; - top: 0; - width: 19px; } - .btn-Radio:hover, .input-Radio.selected-Radio .btn-Radio, .btn-Check:hover, .input-Check.selected-Check .btn-Check { - background: #e93250; } - -.btn-Radio { - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - border-radius: 15px; - background-clip: padding-box; } - -.input-Radio.selected-Radio .btn-Radio { - border-color: #333; } - -.input-Check.selected-Check .btn-Check { - border-color: #333; } - -.ifChecked { - visibility: hidden; } - -.selected-Check .ifChecked { - visibility: visible; } - -/* Drop Down Selection Inputs */ -.select-Container { - background: url(../img/drop-Arrow.png) no-repeat 99% center white; - display: inline-block; } - -.input-Select { - background: none; - border: 0; - font-size: 0.9em; - padding-right: 16px; - width: 100%; - -webkit-appearance: textarea; } - -.file-Input-Area { - position: relative; } - .file-Input-Area label { - cursor: default; } - -.input-File { - cursor: pointer; - left: 0; - height: 100%; - opacity: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 9; } - -.input-File-Text { - cursor: pointer; - display: inline-block; } - -/* Inputs not supported in all browsers */ -.input-Color { - border: 1px solid #cccccc; - font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; - margin-bottom: 8px; - padding: 0 4px; - height: 45px; - width: 50%; } - .input-Color:focus { - background: #f8f8f8; - border: 1px solid #50B6D5; - -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; - -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; - box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; } - -.input-Number { - font-size: 0.9em; } - -::-webkit-input-placeholder { - /* WebKit browsers */ - color: #5e7f8d; - font-style: italic; } - -:-moz-placeholder { - /* Mozilla Firefox 4 to 18 */ - color: #5e7f8d; - font-style: italic; } - -::-moz-placeholder { - /* Mozilla Firefox 19+ */ - color: #5e7f8d; - font-style: italic; } - -:-ms-input-placeholder { - /* Internet Explorer 10+ */ - color: #5e7f8d; - font-style: italic; } - -.btn, .btn-Radio, .btn-Check, .btn-Small, .btn-Large, .btn-XLarge, .btn-Wide, .btn-Action, .btn-Action-2, .btn-Action-3, .btn-Neg, -.btn-Neg:hover, .btn-Disable, .btn-Disable:hover, .btn-Disable:visited, .btn-Square, .btn-Round { - background: #343434; - -moz-border-radius: 18px; - -webkit-border-radius: 18px; - border-radius: 18px; - background-clip: padding-box; - -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25); - -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25); - box-shadow: 0 0 6px rgba(0, 0, 0, 0.25); - color: #f8f8f8; - cursor: pointer; - display: inline-block; - font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; - font-size: 0.9em; - line-height: 1; - padding: 8px 20px; - text-align: center; - text-decoration: none !important; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4); - -moz-transition: all 0.25s ease-in-out 0s; - -ms-transition: all 0.25s ease-in-out 0s; - -o-transition: all 0.25s ease-in-out 0s; - -webkit-transition: all 0.25s ease-in-out 0s; - transition: all 0.25s ease-in-out 0s; - vertical-align: middle; - -webkit-appearance: none; } - .btn.btn-Pad, .btn-Pad.btn-Radio, .btn-Pad.btn-Check, .btn-Pad.btn-Small, .btn-Pad.btn-Large, .btn-Pad.btn-XLarge, .btn-Pad.btn-Wide, .btn-Pad.btn-Action, .btn-Pad.btn-Action-2, .btn-Pad.btn-Action-3, .btn-Pad.btn-Neg, .btn-Pad.btn-Disable, .btn-Pad.btn-Square, .btn-Pad.btn-Round { - padding: 8px 16px; } - .btn:hover, .btn:active, .btn-Radio:hover, .input-Radio.selected-Radio .btn-Radio, .btn-Check:hover, .input-Check.selected-Check .btn-Check, .btn-Small:hover, .btn-Large:hover, .btn-XLarge:hover, .btn-Wide:hover, .btn-Action:hover, .btn-Action-2:hover, .btn-Action-3:hover, .btn-Neg:hover, .btn-Disable:hover, .btn-Square:hover, .btn-Round:hover, .btn-Radio:active, .btn-Check:active, .btn-Small:active, .btn-Large:active, .btn-XLarge:active, .btn-Wide:active, .btn-Action:active, .btn-Action-2:active, .btn-Action-3:active, .btn-Neg:active, .btn-Disable:active, .btn-Square:active, .btn-Round:active { - background: #131313; - color: #f8f8f8; } - .btn:visited, .btn-Radio:visited, .btn-Check:visited, .btn-Small:visited, .btn-Large:visited, .btn-XLarge:visited, .btn-Wide:visited, .btn-Action:visited, .btn-Action-2:visited, .btn-Action-3:visited, .btn-Neg:visited, .btn-Disable:visited, .btn-Square:visited, .btn-Round:visited { - color: #f8f8f8; } - -.btn-Small { - font-size: 0.875em; - padding: 4px 8px; } - .btn-Small.btn-Pad { - padding: 4px 12px; } - -.btn-Large { - font-size: 2em; - padding: 12px; } - .btn-Large.btn-Pad { - padding: 12px 20px; } - -.btn-XLarge { - font-size: 2.5em; - padding: 12px 16px; } - .btn-XLarge.btn-Pad { - padding: 16px 24px; } - -.btn-Wide { - display: block; } - -.btn-Action { - background: #e93250; - background: -webkit-linear-gradient(top, #e93250, #b11f36); - background: -moz-linear-gradient(top, #e93250, #b11f36); - background: -o-linear-gradient(top, #e93250, #b11f36); - background: linear-gradient(top, #e93250, #b11f36); } - .btn-Action:hover { - background: -webkit-linear-gradient(top, #d51c3a, #841526); - background: -moz-linear-gradient(top, #d51c3a, #841526); - background: -o-linear-gradient(top, #d51c3a, #841526); - background: linear-gradient(top, #d51c3a, #841526); } - -.btn-Action-2 { - background: #daeea5; } - .btn-Action-2:hover { - background: #c0e265; } - -.btn-Action-3 { - background: #f6f0e6; } - .btn-Action-3:hover { - background: #e2ceae; } - -.btn-Neg, -.btn-Neg:hover { - background: #aaaaaa; - color: #f8f8f8; } - -.btn-Disable, .btn-Disable:hover, .btn-Disable:visited { - background: #fafafa; - border: 1px solid #eee; - color: #343434; - cursor: default; } - -.btn-Square { - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - background-clip: padding-box; } - -.btn-Round { - -moz-border-radius: 12px; - -webkit-border-radius: 12px; - border-radius: 12px; - background-clip: padding-box; } - -.nav ul, -.nav ol { - border: 1px solid #ccc; - padding: 4px; } -.nav li { - margin: 0; } -.nav a { - display: block; - padding: 4px 8px; - text-decoration: underline; } - .nav a:hover { - text-decoration: none; } - -.h-Nav > li, .bread > li { - float: left; } - -.c-Nav { - font-size: 0; - text-align: center; } - -.c-Nav > li, .ah-Nav a { - display: inline-block; - font-size: 16px; } - -.v-Nav { - /* really only needs styling applied to it, as vertical nav is default */ } - -.ah-Nav { - font-size: 0; } - -.av-Nav .drop-Start { - display: block; } - -.drop-Start { - position: relative; } - .drop-Start:hover { - background: #eee; } - .drop-Start:hover .drop-Menu { - visibility: visible; } - .drop-Start a { - white-space: nowrap; } - -.drop-Menu { - background: #eee; - border: 1px solid #ddd; - position: absolute; - visibility: hidden; - z-index: 5; } - -.v-Nav .drop-Menu, .av-Nav .drop-Menu { - left: 100%; - top: 0; } - -.h-Nav .drop-Menu, .bread .drop-Menu, .ah-Nav .drop-Menu { - left: 0; - top: 100%; } - -.bread li:last-child:after { - content: none; } -.bread li:after { - content: ">"; - display: inline-block; } -.bread a { - display: inline-block; } -.bread span { - display: inline-block; - padding: 4px 8px; } - -.float-Left { - float: left; } - -.float-Right { - float: right; } - -.float-None { - float: none; } - -.clear { - clear: both; } - -.clearfix, .row, .c-Row, nav ul, -nav ol, .media, .media-Reverse, .media-No-Wrap-Reverse, .media-No-Wrap { - *zoom: 1; } - .clearfix:before, .row:before, .c-Row:before, nav ul:before, - nav ol:before, .media:before, .media-Reverse:before, .media-No-Wrap-Reverse:before, .media-No-Wrap:before, .clearfix:after, .row:after, .c-Row:after, nav ul:after, - nav ol:after, .media:after, .media-Reverse:after, .media-No-Wrap-Reverse:after, .media-No-Wrap:after { - content: ""; - display: table; } - .clearfix:after, .row:after, .c-Row:after, nav ul:after, - nav ol:after, .media:after, .media-Reverse:after, .media-No-Wrap-Reverse:after, .media-No-Wrap:after { - clear: both; } - -.overflow { - overflow: hidden; } - -blockquote, h1, h2, h3, h4, h5, h6, li, p, small, code, kbd, pre, samp, dt, form, table { - margin-bottom: 12px; } - -.d-Block, .show { - display: block; } - -.d-None, .mobile-Show { - display: none; } - -.hidden { - display: none !important; - visibility: hidden; } - -.invis { - border: 0 !important; - height: 1px !important; - left: -999999px !important; - overflow: hidden !important; - position: absolute !important; - width: 1px !important; } - -.pic, .pic-Rounded { - border: 1px solid white; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background-clip: padding-box; - -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - display: inline-block; - overflow: hidden; - width: 90px; } - .pic img, .pic-Rounded img { - display: block; } - -.pic-Rounded { - -moz-border-radius: 100px; - -webkit-border-radius: 100px; - border-radius: 100px; - background-clip: padding-box; } - -/* - Code Example - -------------------------- - - - -*/ -.media, .media-Reverse, .media-No-Wrap-Reverse, .media-No-Wrap { - margin-bottom: 16px; - position: relative; - z-index: 2; } - .media .media-Text > :last-child, .media-Reverse .media-Text > :last-child, .media-No-Wrap-Reverse .media-Text > :last-child, .media-No-Wrap .media-Text > :last-child { - margin-bottom: 0; } - .media .media-Object, .media-Reverse .media-Object, .media-No-Wrap-Reverse .media-Object, .media-No-Wrap .media-Object { - float: left; - margin: 0 16px 8px 0; - max-width: 30%; } - -.media-Reverse .media-Object, .media-No-Wrap-Reverse .media-Object { - float: right; - margin: 0 0 8px 16px; - max-width: 30%; } - -.media-No-Wrap { - padding-left: 100px; } - .media-No-Wrap .media-Text { - float: left; - width: 100%; } - .media-No-Wrap .media-Object { - margin-left: -100px; - margin-right: 0; } - -.media-No-Wrap-Reverse { - padding-right: 100px; } - .media-No-Wrap-Reverse .media-Object { - margin-right: -100px; - margin-left: 0; } - -/* - Code Example - -------------------------- -
-
- -
-
-

Title for Meida Element

-

Paragraph text to go along with media element.

-
-
- - -- Divs were used in this example, but the media element could be applied to various situations. - Ie. could be used for a header or footer area where a logo is placed on the right or left w/text to the side. -*/ -.emph-Block { - background: #fafafa; - border: 1px solid #ddd; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background-clip: padding-box; - -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - margin-bottom: 16px; - min-height: 50px; - padding: 16px; } - .emph-Block > :last-child { - margin-bottom: 0; } - -.kill-List, .intro-List { - list-style: none; - padding: 0; } - -.numbered { - list-style-type: decimal; } - -.shout-Out { - background: #f8f8f8; - border-left: 4px solid #343434; - padding: 16px; } - .shout-Out:before, .shout-Out:after { - display: none; } - -.action-Link:after { - content: "\00A0" "\00BB"; } - -.txt-Center { - text-align: center; } - -.txt-Left { - text-align: left; } - -.txt-Right { - text-align: right; } - -.txt-Just { - text-align: justify; } - -.highlight, .slant { - background: yellow; - color: #131313; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - background-clip: padding-box; - display: inline-block; - padding: 4px; - vertical-align: text-top; } - -.slant { - -moz-transform: skewX(-16deg); - -ms-transform: skewX(-16deg); - -o-transform: skewX(-16deg); - -webkit-transform: skewX(-16deg); - transform: skewX(-16deg); } - -.text-Correct { - display: inline-block; - -moz-transform: skewX(16deg); - -ms-transform: skewX(16deg); - -o-transform: skewX(16deg); - -webkit-transform: skewX(16deg); - transform: skewX(16deg); } - -/* - Code Example: Slanted Text w/Highlight - -------------------------- - - Text Goes here - - - Code Example: Normal flowing text w/slanted highlight - -------------------------- - - Text Goes here - -*/ -.multi { - -moz-column-count: 3; - -webkit-column-count: 3; - column-count: 3; - -moz-column-gap: 40px; - -webkit-column-gap: 40px; - column-gap: 40px; - -moz-column-rule: 1px outset #eeeeee; - -webkit-column-rule: 1px outset #eeeeee; - column-rule: 1px outset #eeeeee; } - -body { - font-family: "Lucida Grande", verdana, "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 300; } - -h1, h2, h3, h4, h5, h6 { - font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif; } - -.giga { - font-size: 4.375em; - font-weight: 700; } - -.ultra { - font-size: 3.75em; - font-weight: 600; } - -.mega { - font-size: 3.5em; - font-weight: 300; } - -h1, .alpha { - font-size: 3.125em; - font-weight: 400; } - -h2, .beta { - font-size: 2.5em; - font-weight: 400; } - -h3, .gamma { - font-size: 2em; - font-weight: 400; } - -h4, .delta { - font-size: 1.625em; - font-weight: 300; } - -h5, .eps { - font-size: 1.375em; - font-weight: 300; } - -h6, .zeta { - font-size: 1.125em; - font-weight: 300; } - -p, li, .base { - font-size: 0.9em; - font-weight: 300; } - -small, .tiny { - font-size: 0.875em; - font-weight: 300; } - -.mini { - font-size: 0.75em; - font-weight: 300; } - -.action-Link { - float: right; } - -body { - background: #CCDF9B; } - -::-moz-selection { - background: #e93250; - color: #eeeeee; - text-shadow: none; } - -::selection { - background: #e93250; - color: #eeeeee; - text-shadow: none; } - -.page { - min-height: 600px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .page { - min-height: 25px; } } - -.content, .footer-Content { - margin: 0 auto; - max-width: 1200px; - padding: 20px; } - .content > :last-child, .footer-Content > :last-child { - margin-bottom: 0; } - -a { - color: #e93250; } - a:hover, a:active { - color: #ba142f; } - a:visited { - color: #A5424B; } - -:focus, a:focus, a:active, input[type="submit"]::-moz-focus-inner { - outline: none; } - -@media (max-device-width: 480px), screen and (max-width: 800px) { - .col-33, .col-40, .col-60 { - float: none; - width: 100%; } } - -@media (max-device-width: 480px), screen and (max-width: 800px) { - .col-50 { - float: none; - padding: 0; - width: 100% !important; } } - -.header-Base { - background: #65a0ad; - border-bottom: 6px solid #e93250; - min-height: 42px; - overflow: hidden; } - -@media (max-device-width: 480px), screen and (max-width: 800px) { - .header-Content { - padding: 0; } } - -.blurb { - background: #fff; } - -.header-Intro { - background: url(../img/neocity.jpg) 95% bottom no-repeat; - min-height: 214px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .header-Intro { - -moz-background-size: cover; - -ms-background-size: cover; - -o-background-size: cover; - -webkit-background-size: cover; - background-size: cover; - min-height: 2px; } } - -.header-Outro { - background: #30424b -moz-linear-gradient(top, #2b3c43 0%, #354751 100%); - /* FF3.6+ */ - background: #30424b -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b3c43), color-stop(100%, #354751)); - /* Chrome,Safari4+ */ - background: #30424b -webkit-linear-gradient(top, #2b3c43 0%, #354751 100%); - /* Chrome10+,Safari5.1+ */ - background: #30424b -o-linear-gradient(top, #2b3c43 0%, #354751 100%); - /* Opera 11.10+ */ - background: #30424b -ms-linear-gradient(top, #2b3c43 0%, #354751 100%); - /* IE10+ */ - background: #30424b linear-gradient(to bottom, #2b3c43 0%, #354751 100%); - /* W3C */ - -moz-box-shadow: inset 0 7px 10px 0 rgba(0, 0, 0, 0.1); - -webkit-box-shadow: inset 0 7px 10px 0 rgba(0, 0, 0, 0.1); - box-shadow: inset 0 7px 10px 0 rgba(0, 0, 0, 0.1); - color: #fafafa; } - -.hp .header-Outro .col-50 { - width: 48%; } - -.hp .header-Outro .signup-Area { - float: right; } - -@media (max-device-width: 480px), screen and (max-width: 800px) { - .intro-List { - margin-bottom: 20px; - padding: 20px !important; } } -.intro-List li { - padding-left: 36px; - padding-right: 12px; - margin-bottom: 20px; } -.intro-List h2 { - margin-bottom: 2px; } -.intro-List p { - color: #B2BCC1; } - -.intro-Icon { - background: url(../img/icons.png) no-repeat; - display: block; - height: 37px; - left: -10px; - position: absolute; - top: 2px; - width: 37px; } - -.intro-Tools { - position: relative; } - -.intro-Question { - position: relative; } - .intro-Question .intro-Icon { - background-position: 0 -40px; } - -.intro-Social { - position: relative; } - .intro-Social .intro-Icon { - background-position: 0 -80px; } - -.signup-Area { - min-height: 100px; - position: relative; } - -.signup-Form { - background: #354751; - border-radius: 4px 4px 0 0; - -moz-box-shadow: 1px 2px 12px 2px rgba(0, 0, 0, 0.15); - -webkit-box-shadow: 1px 2px 12px 2px rgba(0, 0, 0, 0.15); - box-shadow: 1px 2px 12px 2px rgba(0, 0, 0, 0.15); - height: 600%; - overflow: hidden; - position: absolute; - top: -45px; - width: 95%; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .signup-Form { - height: auto; - margin: 0; - overflow: visible; - padding-bottom: 20px; - position: static; - width: auto; } } - .signup-Form h2 { - margin-bottom: 0; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); - font-size: 1.8em; } - .signup-Form hr { - border-bottom: 1px solid #4a6677; - border-top: 1px solid #1d282d; - margin: 4px 0 22px; } - .signup-Form fieldset { - background: url(../img/sign-up-bg.png) repeat-x center top; - padding: 20px 33px; } - .signup-Form label { - color: #81b8c6; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .signup-Form label { - font-size: 70%; } } - .signup-Form .input-Area { - background: #29383f; - border: 0 solid black; - -moz-box-shadow: inset 1px 3px 10px 0px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: inset 1px 3px 10px 0px rgba(0, 0, 0, 0.2); - box-shadow: inset 1px 3px 10px 0px rgba(0, 0, 0, 0.2); - color: #557380; - margin-bottom: 28px; - margin-right: 4px; - padding: 11px 12px 9px 12px; - width: 62%; } - .signup-Form .input-Area:focus { - color: #eee; } - .signup-Form .btn-Action { - padding: 10px 25px; } - -.small-Nav { - background: #30424B; - display: none; - position: fixed; - right: 0; - top: 0; - width: 50px; - z-index: 9999; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .small-Nav { - display: block; } } - -.header-Nav { - background: #5e95a1; - border-bottom: 1px solid #92B4BD; - -moz-transition: all 0.35s; - -ms-transition: all 0.35s; - -o-transition: all 0.35s; - -webkit-transition: all 0.35s; - transition: all 0.35s; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .header-Nav { - position: fixed; - top: -900px !important; } } - .header-Nav.show-Nav { - top: 0 !important; } - .header-Nav a, .header-Nav a:visited { - color: #fff; - padding: 8px 12px; - text-decoration: none; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .header-Nav a, .header-Nav a:visited { - display: block; } } - .header-Nav a:hover, .header-Nav a:visited:hover { - background: #528995; - text-decoration: underline; } - .header-Nav a.selected, .header-Nav a:active, .header-Nav a:visited.selected, .header-Nav a:visited:active { - background: #528995; - text-decoration: underline; } - -.constant-Nav { - float: left; - position: relative; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .constant-Nav { - float: none; } - .constant-Nav li { - float: none; } } - -.status-Nav { - float: right; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .status-Nav { - float: none; } } - .status-Nav li { - float: left; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .status-Nav li { - float: none; } } - -.hp .header-Base { - padding-top: 46px; } - -.hp .header-Nav { - left: 0; - position: fixed; - top: 0; - width: 100%; - z-index: 3; } - -.hp .hp-Logo { - left: -90px; - position: fixed; - -moz-transition: all 0.35s; - -ms-transition: all 0.35s; - -o-transition: all 0.35s; - -webkit-transition: all 0.35s; - transition: all 0.35s; } - .hp .hp-Logo.in-View { - left: 0 !important; - z-index: 99; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .hp .hp-Logo.in-View { - left: -90px !important; } } - -.hp .logo { - padding-top: 45px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .hp .logo { - padding-left: 20px; - padding-right: 20px; } } - -.constant-Nav { - margin-left: -88px; - -moz-transition: all 0.35s; - -ms-transition: all 0.35s; - -o-transition: all 0.35s; - -webkit-transition: all 0.35s; - transition: all 0.35s; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .constant-Nav { - margin-left: 0; } } - .constant-Nav.in-View { - margin-left: 0; - padding-left: 70px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .constant-Nav.in-View { - padding-left: 0; } } - -.add-Stripe { - border-bottom: 6px solid #E93250; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .add-Stripe { - border: 0; } } - -.interior .page { - padding-top: 22px; - float: left; - width: 100%; } - -.interior .signup-Form { - top: 0; } - -.interior .header-Base { - left: 0; - overflow: visible; - position: fixed; - top: 0; - width: 100%; - z-index: 9; } - -.int-Logo { - left: 0; - position: absolute; - top: 0; - width: 70px; - z-index: 9; } - -.interior .header-Nav { - padding-left: 70px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .interior .header-Nav { - width: 100%; } } - -.interior .constant-Nav { - margin: 0; } - -.content-Base { - background: #f6f0e6; - min-height: 500px; - padding-bottom: 50px; - padding-top: 10px; } - .content-Base h1, .content-Base h2, .content-Base h3, .content-Base h4, .content-Base h5, .content-Base h6 { - color: #e93250; } - -.content, .footer-Content, .footer-Content { - padding: 20px 3%; } - -.single-Col { - max-width: 800px; } - -.twitter-tweet.twitter-tweet-rendered { - margin: 0 auto 30px !important; } - -.interior .header-Outro { - padding-top: 30px; - overflow: hidden; } - -.interior .header-Outro h1 { - font-size: 2.5em; - margin-top: 15px; } - -.site-url { - font-size: 18px; - margin-bottom: 8px; } - -.site-url a { - color: #e93250; - font-weight: bold; } - -.interior .header-Outro .subtitle { - font-size: 1em; - margin-top: -15px; } - -.content.wide, .wide.footer-Content { - padding-left: 6%; - padding-right: 6%; } - -.content.misc-page, .misc-page.footer-Content { - background: #FAF6F1; - -moz-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); - -webkit-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); - box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); - padding: 1px 3% 40px 3%; } - -.content.misc-page h3, .misc-page.footer-Content h3, .content.misc-page h4, .misc-page.footer-Content h4, .content.misc-page h5, .misc-page.footer-Content h5, .content.misc-page h6, .misc-page.footer-Content h6 { - margin-top: 1em; } - -.content.misc-page h2, .misc-page.footer-Content h2 { - font-size: 1.6em; - margin-top: 1em; } - -.content.misc-page h3, .misc-page.footer-Content h3, .content.misc-page h4, .misc-page.footer-Content h4, .content.misc-page h5, .misc-page.footer-Content h5 { - font-size: 1.2em; } - -.content.misc-page h6, .misc-page.footer-Content h6 { - font-size: 1em; } - -.content.misc-page h1, .misc-page.footer-Content h1 { - font-size: 2.2em; - margin-bottom: 20px; } - -.content.misc-page hr, .misc-page.footer-Content hr { - margin: 35px 0 25px 0; } - -.interior .header-Outro .col.col-50.signup-Area { - padding-left: 0; } - -.interior .header-Outro a { - color: #E93250; } - -.interior .header-Outro .btn-Action { - color: #fff; } - -.interior .signup-Area { - min-height: 164px; } - -.interior .signup-Area img { - -moz-box-shadow: 1px 2px 5px 2px rgba(0, 0, 0, 0.1); - -webkit-box-shadow: 1px 2px 5px 2px rgba(0, 0, 0, 0.1); - box-shadow: 1px 2px 5px 2px rgba(0, 0, 0, 0.1); - border: 4px solid white; } - -.interior .signup-Area.large img { - border: 6px solid white; } - -.interior .header-Outro .screenshot { - width: 235px; - height: 141px; } - -.interior .header-Outro ul { - margin: 0; - padding: 0; } - -.interior .header-Outro ul li { - list-style: none; - font-size: 80%; - margin-bottom: 2px; } - -.interior .signup-Form fieldset { - padding: 20px; } - -.interior .header-Outro h2 { - margin-top: 12px; - font-size: 1.8em; } - -.welcome { - background: #daeea5 url(../img/heartcat.png) no-repeat 20px center; - background-size: 77px 81px; - padding: 20px 20px 20px 108px; - margin-bottom: 30px; } - -.welcome h4 { - font-size: 1.2em; - margin-bottom: 4px; } - -.welcome .close-button { - float: right; - background: url(../img/welcome-close.png) no-repeat; - width: 19px; - height: 19px; } - -.files { - float: left; - background: #E4D8CB; - width: 100%; - position: relative; } - -.files .header { - background: #5E95A1; - color: #fff; - float: left; - width: 100%; - padding: 10px 20px; } - -.files .breadcrumbs { - float: left; - font-weight: bold; - margin-top: 4px; } - -.files .actions { - float: right; } - -.files .btn-Action { - margin-left: 8px; } - -.btn-Action span { - background-repeat: no-repeat; } - -.btn-Action.new-Page span { - background-image: url(../img/new-page.png); - background-position-y: 1px; - padding-left: 29px; } - -.btn-Action.new-Folder span { - background-image: url(../img/new-folder.png); - padding-left: 26px; } - -.btn-Action.upload span { - background-image: url(../img/upload.png); - padding-left: 24px; } - -.files .list { - padding: 20px; } - -.files .list .upload-Boundary { - float: left; - border: 3px dashed #F6F0E6; - width: 100%; - margin: 18px 0; - padding: 10px; - -webkit-border-radius: 8px; - -moz-border-radius: 8px; - border-radius: 8px; - min-height: 300px; } - -.files .list .upload-Boundary.with-instruction { - background: url(../img/drag-drop.png) no-repeat center center; } - -.files .uploading-overlay { - width: 100%; - height: 100%; - position: absolute; - background-color: rgba(0, 0, 0, 0.35); - z-index: 100; } - -.files .uploading { - background: #fff; - font-style: italic; - margin-left: auto; - margin-right: auto; - width: 400px; - margin-top: 14%; - padding: 25px 40px 28px 40px; - -webkit-box-shadow: 1px 1px 21px 5px rgba(50, 50, 50, 0.5); - -moz-box-shadow: 1px 1px 21px 5px rgba(50, 50, 50, 0.5); - box-shadow: 1px 1px 21px 5px rgba(50, 50, 50, 0.5); - border-radius: 10px; } - -.files .uploading p { - margin-bottom: 2px; } - -.files .progress-bar { - background: #CCCCCC; - -webkit-border-radius: 8px; - -moz-border-radius: 8px; - border-radius: 8px; - width: 100%; - position: relative; - margin-top: 14px; - height: 10px; - overflow: hidden; } - -.files .progress-bar .progress { - background: #E93250; - height: 100%; - -webkit-border-top-right-radius: 0px; - -webkit-border-bottom-right-radius: 0px; - -moz-border-radius-topright: 0px; - -moz-border-radius-bottomright: 0px; - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; - -webkit-border-top-left-radius: 20px; - -webkit-border-bottom-left-radius: 20px; - -moz-border-radius-topleft: 20px; - -moz-border-radius-bottomleft: 20px; - border-top-left-radius: 20px; - border-bottom-left-radius: 20px; - position: relative; - overflow: hidden; - display: block; } - -.file { - float: left; - padding: 5px 0px; - margin-right: 10px; - margin-bottom: 10px; - width: 125px; - text-align: center; - display: block; - position: relative; } - -.file .title { - font-weight: bold; - font-size: 12px; - color: #666; - margin-top: 4px; - text-decoration: none; - white-space: nowrap; - overflow: hidden; - display: block; } - -.html-thumbnail { - font-size: 11px; - margin-top: 5px; - margin-left: 10px; - display: block; - position: relative; - width: 105px; - height: 63px; } - -.html-thumbnail.html img { - width: 105px; - height: 63px; - -webkit-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); - box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); } - -.html-thumbnail.image img { - max-width: 105px; - max-height: 63px; - width: auto; - height: auto; } - -.html-thumbnail.misc { - width: 63px; - height: 63px; - margin-left: auto; - margin-right: auto; } - -.misc-icon { - background: url(../img/misc-file.png) no-repeat 0px 0px; - width: 67px; - height: 67px; - display: block; - padding-top: 35px; - font-size: 14px; - color: #bbb; - font-weight: bold; - margin-left: auto; - margin-right: auto; } - -.folder-icon { - background: url(../img/folder.png) no-repeat 0px 0px; - width: 95px; - height: 65px; - display: block; - padding-top: 35px; } - -.overlay a { - color: white; - text-decoration: none; - font-size: 14px; - display: block; } - -.overlay i { - font-weight: bold; } - -.overlay { - position: absolute; - top: 0; - width: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - display: none; } - -.link-overlay { - display: block; - position: absolute; - bottom: 0px; - left: 0px; - width: 123px; - height: 30px; } - -.file > .overlay { - -webkit-border-radius: 8px; - -moz-border-radius: 8px; - border-radius: 8px; - padding-top: 15px; - background-color: rgba(0, 0, 0, 0.15); } - -.filehover:hover .overlay, .fileimagehover:hover .overlay { - display: block; } - -.html-thumbnail.misc.fileimagehover .overlay { - margin: 1px 0 0 2px; } - -.site-actions { - float: left; - margin-top: 20px; - font-size: 90%; } - -.site-actions a { - color: #666; } - -@media (min-width: 1200px) { - .container { - width: 1200px; } } -@media (min-width: 700px) and (max-width: 1200px) { - .container { - width: 100%; } } -.content.misc-page.columns, .misc-page.columns.footer-Content { - float: left; - padding: 0; - position: relative; - clear: both; - width: 100%; - overflow: hidden; } - -.col-left { - float: left; - width: 100%; - position: relative; - border-right: 1px solid #ddd; } - -.right-col { - background: #FAF6F1; } - -.content.misc-page .col-33 h3, .misc-page.footer-Content .col-33 h3 { - font-size: 1.5em; } - -.content.misc-page .col-33 h3:nth-of-type(1), .misc-page.footer-Content .col-33 h3:nth-of-type(1) { - margin-top: 0; } - -.right-col .col-left { - background-color: white; - right: 33%; } - -.content.misc-page.columns .col, .misc-page.columns.footer-Content .col { - padding: 25px 30px 30px 30px; - position: relative; - overflow: hidden; } - -.content.misc-page.columns .col-66, .misc-page.columns.footer-Content .col-66 { - width: 67%; - left: 33%; } - -.content.misc-page.columns .col-33, .misc-page.columns.footer-Content .col-33 { - width: 33%; - left: 33%; } - -.interior .header-Outro.with-columns { - padding-top: 22px; } - -.interior .header-Outro.with-columns h3 { - float: left; - margin-bottom: 0; - font-size: 1.7em; } - -.interior .header-Outro.with-columns .content, .interior .header-Outro.with-columns .footer-Content { - padding: 0; } - -.interior .header-Outro.with-columns .col { - padding: 25px 0 8px 30px; } - -.interior .header-Outro.with-columns .col-32 { - width: 33%; } - -.interior .header-Outro.with-columns .col-66 { - width: 67%; - border-right: 1px solid #0B0F11; } - -.interior .header-Outro.with-columns .col-32 .edit { - margin-top: 4px; - float: right; - margin-right: 30px; - color: #fff; } - -.feed-filter { - float: left; - margin-top: 1.4em; - font-size: 0.8em; - margin-left: 1.5em; } - -.interior .header-Outro .feed-filter a { - color: white; - margin-left: 13px; } - -.site-suggestion { - float: left; - width: 156px; - margin-right: 20px; - margin-bottom: 20px; - height: 160px; } - -.stats { - margin-bottom: 2em; - float: left; - width: 100%; } - -.content.misc-page.columns .stats .col, .misc-page.columns.footer-Content .stats .col { - padding: 0; - margin-bottom: 2em; } - -.large-portrait { - border: 10px solid white; - -moz-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); } - -.site-portrait { - border: 5px solid white; - -moz-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - float: left; - margin-bottom: .5em; } - -.site-portrait img { - width: 146px; } - -.site-portrait .caption { - display: block; - clear: both; - font-size: .8em; - margin-top: .2em; - margin-bottom: -.2em; } - -a.tag { - font-size: .7em; - text-transform: uppercase; - background: #FFFFCC; - color: #C1A009; - float: left; - padding: 1px 5px; - -moz-box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2); - box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2); - margin-right: 3px; - margin-right: 10px; - margin-bottom: 7px; } - -a.tag:hover { - text-decoration: none; - background: #FFEE8A; } - -.following { - width: 100%; - float: left; - margin-bottom: 2em; } - -.avatar { - height: 37px; - width: 37px; - margin: 0 4px 4px 0; - float: left; } - -.news-item { - width: 100%; - float: left; } - -.news-item.update, .news-item.tags, .news-item.comment.for-me, .news-item.tip.for-me { - margin: 1.8em 0; } - -.news-item:nth-of-type(1) { - margin-top: 0; } - -.news-item.first { - margin-top: 0; } - -.news-item .user { - font-weight: bold; - color: #000; } - -.news-item .comment { - color: #000; - font-style: normal; - font-size: .8em; } - -.news-item.for-me .comment { - color: #000; - font-style: normal; - margin-top: .2em; - font-size: .9em; - margin-bottom: -.4em; } - -.news-item .icon { - width: 20px; - height: 20px; - background: #E6E6E6; - float: left; - margin-right: 12px; } - -.news-item.comment .icon { - background: #DAEEA5; } - -.news-item.comment.for-me .icon, .news-item.tip.for-me .icon { - background-size: 62px 62px; - width: 82px; - height: 62px; - background-position: right top; - background-repeat: no-repeat; } - -.news-item.update .icon { - background: #E93250; } - -.news-item.tip .icon { - background: #FFCC00; } - -.news-item.follow .icon { - background: #3399CC; } - -.news-item .title { - margin-bottom: .4em; - position: relative; - width: 100%; - float: left; } - -.news-item .date { - float: right; - color: #aaa; - font-size: 11px; - margin-top: .5em; } - -.news-item .files { - background: #eee; - padding: 8px 10px 2px 10px; - border-top: 1px solid #ddd; - margin-bottom: 4px; } - -.news-item .file { - width: 90px; - margin-bottom: 0; - padding: 0; } - -.news-item .file a:hover { - text-decoration: none; } - -.news-item .html-thumbnail { - margin-top: 1px; - margin-left: 0px; - width: auto; } - -.news-item .site-suggestion { - height: auto; - margin-bottom: 10px; } - -.news-item .tag { - float: none; - margin-left: 4px; } - -.news-item .actions { - font-size: 11px; } - -.news-item .actions a { - margin-right: 6px; } - -.news-item .content, .news-item .footer-Content { - padding: 0 0 0 32px; } - -.news-item .comments { - margin-bottom: 1.5em; - margin-top: .7em; } - -.news-item .comments .comment { - font-size: .8em; - clear: both; } - -.news-item .comments .comment .user { - margin-right: 5px; } - -.news-item .comments .comment .actions { - margin-top: .3em; } - -.news-item .avatar { - margin-right: 10px; } - -.signup-Area.large { - width: 418px; - height: 236px; } - -.interior .header-Outro.with-site-image { - padding-top: 20px; } - -.report { - margin-top: 2em; - float: left; - width: 100%; - font-size: .8em; } - -.report, .report a { - color: #999; } - -.interior .header-Outro .actions a { - margin-right: 6px; } - -.interior .header-Outro .stats { - margin-bottom: 1.2em; - float: left; - width: 100%; - margin-top: 2em; } - -.interior .header-Outro .stats strong { - font-size: 1.5em; - font-weight: bold; - color: #DAEEA5; } - -.interior .header-Outro .stats span { - font-size: .7em; - text-transform: uppercase; - clear: both; - display: block; } - -.interior .header-Outro .stats .stat { - float: left; - width: 90px; - text-align: center; } - -.interior .header-Outro .stats .stat.tips { - width: 60px; } - -.btn-Action.follow span { - background-image: url(../img/follow.png); - padding-left: 22px; } - -.btn-Action.tip span { - background-image: url(../img/tip.png); - padding-left: 26px; - background-position-y: -1px; } - -.btn-Action.share span { - background-image: url(../img/share.png); - padding-left: 26px; } - -.col-33 .stats .stat { - margin-bottom: .4em; } - -.col-33 .stats .stat span { - width: 10em; - float: left; } - -.archives { - float: left; - width: 100%; - margin-bottom: 2em; } - -.archives img { - float: left; - border: 3px solid white; - -webkit-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); - box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); - width: 72px; } - -.archives img { - margin-right: 10px; } - -.more { - float: left; - clear: both; - margin-top: 1em; - font-size: .8em; } - -.post-comment { - background: #DAEEA5; - float: left; - width: 111%; - padding: 20px 0 10px 30px; - margin: -25px 0 28px -30px; } - -.post-comment input { - width: 84%; - float: left; } - -.post-comment .btn-Action { - margin-left: 10px; - margin-top: 4px; } - -.supporter-badge { - background: url(../img/supporter.png); - width: 99px; - height: 26px; - float: left; - margin-top: 7px; - margin-left: 10px; } - -.title-with-badge { - float: left; - width: 100%; } - -.title-with-badge span { - float: left; } - -.section.featured-Websites { - background: #65A0AD; } - -.section.featured-Websites h2 { - color: white; } - -.section { - padding: 50px 10%; - margin: 0 auto; } - -.section.previews { - background: #f6f0e6; } - -.section.previews h2, .section.previews p { - color: #31424B; } - -.section h2 { - font-size: 1.6em; } - -.section.last { - background: #666666; } - -.section.last h2, .section.last blockquote { - color: white; } - -.selected { - font-weight: bold; } - -.modal-body { - max-height: 800px; - overflow-y: visible; } - -.tt-dropdown-menu { - padding: 0px 10px 0px 10px; - background: #FFFFFF; - cursor: pointer; } - -.interior .header-Outro.browse-page { - overflow: visible; } - -.interior .header-Outro.browse-page .select-Container { - overflow: none; - padding: 2px 4px; - width: 40%; } - -.browse-page .input-Area { - width: 100%; - padding: 4px 5px; } - -.header-Outro .twitter-typeahead { - top: -2px; - color: #000; } - -.header-Outro .btn-Action { - margin: 0 0 0 10px; } - -.browse-page .text-Label, .browse-page .option-Container { - float: left; - margin-right: 8px; - font-weight: bold; - line-height: 31px; } - -.browse-page h1 { - margin-top: 0; } - -.interior .header-Outro.with-columns .col.filter { - padding-top: 0px; - padding-bottom: 4px; } - -.footer-Base { - color: #5e5b56; - float: left; - width: 100%; } - .footer-Base h1, .footer-Base h2, .footer-Base h3, .footer-Base h4 { - color: #8b9a7a; } - -.footer-Intro { - background: #daeea5; - border-top: 1px solid #cedbab; - -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); - position: relative; } - -.footer-Content { - padding-top: 20px; - padding-bottom: 20px; - padding-left: 40px; - padding-right: 20px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .footer-Content { - padding-left: 20px; } } - -.footer-Content .row, .footer-Content .c-Row { - margin-left: 0; } - -.f-Col { - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - box-sizing: content-box; - min-height: 125px; - padding-bottom: 28px; - position: relative; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .f-Col { - min-height: 2px; - padding-left: 15px !important; - padding-right: 100px !important; } } - .f-Col .action-Link { - bottom: 0; - position: absolute; - right: 12px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .f-Col .action-Link { - position: static; } } - -.footer-icon { - background-repeat: no-repeat; - position: absolute; - right: 0; - top: -70px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .footer-icon { - -moz-background-size: 100%; - -ms-background-size: 100%; - -o-background-size: 100%; - -webkit-background-size: 100%; - background-size: 100%; - height: 90px !important; - top: 0; - width: 77px !important; } } - -.f-Col-1 { - padding-right: 12px; } - .f-Col-1 .footer-icon { - background-image: url(../img/support-us.png); - height: 104px; - right: 5px; - width: 92px; } - -.f-Col-2 { - padding-left: 15px; - padding-right: 6px; - position: relative; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .f-Col-2 { - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - border-top: 1px solid rgba(0, 0, 0, 0.1); - margin: 20px 0; - overflow: hidden; - padding: 20px 0; } - .f-Col-2 .footer-icon { - top: 14px; } } - .f-Col-2:before, .f-Col-2:after { - background: url("../img/border.png") no-repeat 0 -20px; - content: ""; - height: 200px; - opacity: 0.2; - position: absolute; - top: 0; - width: 1px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .f-Col-2:before, .f-Col-2:after { - display: none; } } - .f-Col-2:before { - left: -14px; } - .f-Col-2:after { - right: -14px; } - .f-Col-2 .footer-icon { - background-image: url(../img/about-neocities.png); - width: 100px; - height: 106px; } - -.f-Col-3 { - padding-left: 20px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .f-Col-3 { - padding-left: 0; } } - .f-Col-3 .footer-icon { - background-image: url(../img/latest-news.png); - height: 103px; - width: 134px; } - -.footer-Outro { - background: #ccdf9b; - border-top: 1px solid #b3c388; - overflow: hidden; } - .footer-Outro a { - color: #5e5b56; } - -.credits { - margin-bottom: 0; } - -.footer-Nav { - text-transform: uppercase; } - .footer-Nav .h-Nav, .footer-Nav .bread { - float: right; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .footer-Nav .h-Nav, .footer-Nav .bread { - float: none; - margin-top: 15px; } - .footer-Nav .h-Nav li:first-child a, .footer-Nav .bread li:first-child a { - padding-left: 0; } } - .footer-Nav .h-Nav a, .footer-Nav .bread a { - padding: 0 8px; } - .footer-Nav .h-Nav li:last-child a, .footer-Nav .bread li:last-child a { - padding-right: 0; } - -.alert { - background-color: #F5BA00; - color: #fff; } - -.website-Gallery { - list-style: none; - padding: 30px 20px; } - .website-Gallery li { - float: left; - margin-bottom: 8px; } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .website-Gallery li { - width: 50%; } } - .website-Gallery li a { - display: block; } - .website-Gallery .site-info a { - color: #666; - font-size: 80%; } - .website-Gallery .title { - margin-top: 8px; - margin-bottom: 2px; } - .website-Gallery.int-Gall li { - margin: 0 .5% 12px; - width: 24%; - background-color: white; - border: 9px solid white; - -moz-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); - box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); } - @media (max-device-width: 480px), screen and (max-width: 800px) { - .website-Gallery.int-Gall li { - width: 49%; } } - -.neo-SS, .neo-Screen-Shot { - border: 1px solid #eee; - display: block; - height: auto !important; - position: relative; - width: 100%; } - -.img-Holder { - -moz-background-size: cover !important; - -webkit-background-size: cover !important; - background-size: cover !important; - display: block; } - -.hp-Gallery img, .neo-Screen-Shot img { - width: 100%; } diff --git a/public/assets/css/neo.min.css b/public/assets/css/neo.min.css deleted file mode 100644 index b20089d2..00000000 --- a/public/assets/css/neo.min.css +++ /dev/null @@ -1,908 +0,0 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,700,700italic,600,400italic,300italic,600italic); -@import url(//fonts.googleapis.com/css?family=Droid+Serif:400,700); -*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - -html { overflow-y: scroll; min-height: 100%; } - -body { line-height: 1.5; } - -body, blockquote, h1, h2, h3, h4, h5, h6, p, pre, form, fieldset, img, legend, table, th, td, caption, hr { border: 0; margin: 0; outline: 0; padding: 0; } - -hr { border-top: 1px solid #ddd; display: block; height: 1px; margin: 24px 0; } - -article, aside, details, figure, figcaption, footer, header, main, nav, section, summary { display: block; } - -[hidden] { display: none; } - -img { color: red; font-style: italic; } - -audio, img, object, embed, video { max-width: 100%; } - -audio, canvas, video { display: inline-block; } - -audio:not([controls]) { display: none; height: 0; } - -svg:not(:root) { overflow: hidden; } - -small { display: block; } - -p small, li small { display: inline; margin: 0; } - -b, strong { font-weight: bold; } - -i, em, dfn { font-style: italic; } - -blockquote { font-size: 1.125em; font-style: italic; } -blockquote:before, blockquote:after { font-size: 1.375em; font-weight: 400; line-height: 1; position: relative; top: 2px; } -blockquote:before { content: '"'; left: -1px; } -blockquote:after { content: '"'; right: -1px; } - -q { quotes: "\201C" "\201D" "\2018" "\2019"; } - -abbr[title], dfn[title] { border-bottom: 1px dotted #cccccc; cursor: help; } - -mark { background: yellow; color: #131313; } - -sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } - -sub { bottom: -.25em; } - -sup { top: -.5em; } - -code, kbd, pre, samp { background: #16414c; border-left: 6px solid #acd473; color: #a9b9b9; display: block; font-family: monospace, serif; font-size: 0.9em; padding: 12px 8px 12px 20px; } - -pre { overflow: auto; white-space: pre-wrap; } -pre mark { background: #eee; border-bottom: 1px solid #ddd; color: #333; } - -p code, p kbd, p pre, p samp, li code, li kbd, li pre, li samp, pre code, pre kbd, pre pre, pre samp { display: inline-block; margin: 0; padding: 4px; } - -.code-Value { color: #24b9af; } - -.code-Tag { color: #8ab04c; } - -ol, ul { margin: 0 0 8px; padding: 0 0 0 24px; } -ol > li:last-child, ul > li:last-child { margin-bottom: 0; } -ol li ol, ol li ul, ul li ol, ul li ul { margin-top: 8px; } -ol li li, ul li li { font-size: 1em; } - -dd { margin: 0 0 8px; padding-left: 16px; } - -button, input, select, textarea { border: 0; font-family: inherit; font-size: 100%; line-height: normal; margin: 0; text-transform: none; } - -button, html input[type='button'], input[type='reset'], input[type='submit'] { cursor: pointer; -webkit-appearance: button; } - -input[type='search'] { -webkit-appearance: textfield; } - -input[disabled] { background: #eeeeee; cursor: not-allowed; } - -input[readonly] { background: #fafafa; } - -input[type='search']::-webkit-search-decoration { -webkit-appearance: none; } - -buton::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } - -textarea { overflow: auto; vertical-align: top; border: 2px solid #DCE4EC; } - -.tooltip-inner { white-space: pre-wrap; } - -table { border-collapse: collapse; border-spacing: 0; } - -.table-Base, .table-Border, .table-Stripe { background: #fafafa; font-size: 0.9em; width: 100%; } -.table-Base th, .table-Border th, .table-Stripe th, .table-Base td, .table-Border td, .table-Stripe td { padding: 4px; text-align: left; } - -.table-Header { background: #eee; } - -.table-Footer { background: #e3e3e3; } - -.table-Border { border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; } -.table-Border tr { border-top: 1px solid #cccccc; } -.table-Border th, .table-Border td { border-left: 1px solid #cccccc; } - -.table-Stripe tr:nth-child(2n) { background: #eeeeee; } - -.row, .c-Row { margin-left: -20px; margin-bottom: 20px; } - -.col { float: left; margin-bottom: 0 !important; padding-left: 20px; position: relative; width: 100%; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .col { float: none; padding: 0; } } - -.c-Row { font-size: 0; text-align: center; } -.c-Row .col { display: inline-block; float: none; font-size: 16px; text-align: left; } - -.col-90 { width: 90%; } - -.col-80 { width: 80%; } - -.col-75 { width: 75%; } - -.col-66 { width: 66.6666%; } - -.col-60 { width: 60%; } - -.col-50 { width: 50%; } - -.col-40 { width: 40%; } - -.col-33 { width: 33.3333%; } - -.col-25, .website-Gallery li { width: 25%; } - -.col-20 { width: 20%; } - -.col-10 { width: 10%; } - -.rfl { padding-left: 300px; } -.rfl .f-Col { float: left; margin-left: -280px; width: 280px; } - -.rfr { padding-right: 300px; } -.rfr .f-Col { float: right; margin-right: -300px; width: 280px; } - -.block { background: #ccc; color: #333; padding: 4px; } -.block > :last-child { margin-bottom: 0; } - -nav ul, nav ol { list-style: none; margin: 0; padding: 0; } -nav li { margin: 0; } -nav a { display: inline-block; padding: 4px 8px; text-decoration: underline; } -nav a:hover { text-decoration: none; } - -.grouping { padding: 4px 0; } - -.fs-Legend { border: 1px solid #cccccc; margin-bottom: 8px; padding: 8px 12px; } - -legend, .legend { font-size: 1.375em; margin-left: -4px; padding: 0 4px; } - -/* Text Input Areas & Labels */ -.text-Label, .option-Container { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; cursor: pointer; display: block; margin-bottom: 4px; } - -.dis-Label { cursor: not-allowed; } - -.input-Area, .text-Area, .select-Container, .input-Number { background: #fff; border: 2px solid #cccccc; font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; line-height: 1.25; margin-bottom: 8px; padding: 8px 4px; width: 80%; } -.input-Area:focus, .text-Area:focus, .select-Container:focus, .input-Number:focus { background: #f8f8f8; border: 2px solid #50B6D5; -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; } - -.text-Area { display: block; min-height: 150px; resize: vertical; width: 100%; } - -/* Check/Radio Inputs & Labels */ -.option-Container { position: relative; } -.option-Container:hover .btn-Radio, .option-Container:hover .btn-Check { border-color: #333; } - -.option-Label { cursor: pointer; display: block; padding-left: 28px; position: relative; z-index: 2; } - -.input-Hide { border: 0 !important; height: 1px !important; left: -999999px !important; overflow: hidden !important; position: absolute !important; width: 1px !important; } - -.btn-Radio, .btn-Check { background: #eeeeee; border: 1px solid #ccc; font-size: 14px; font-weight: bold; height: 19px; left: 0; padding: 2px; position: absolute; top: 0; width: 19px; } -.btn-Radio:hover, .input-Radio.selected-Radio .btn-Radio, .btn-Check:hover, .input-Check.selected-Check .btn-Check { background: #e93250; } - -.btn-Radio { -moz-border-radius: 15px; -webkit-border-radius: 15px; border-radius: 15px; background-clip: padding-box; } - -.input-Radio.selected-Radio .btn-Radio { border-color: #333; } - -.input-Check.selected-Check .btn-Check { border-color: #333; } - -.ifChecked { visibility: hidden; } - -.selected-Check .ifChecked { visibility: visible; } - -/* Drop Down Selection Inputs */ -.select-Container { background: url(../img/drop-Arrow.png) no-repeat 99% center white; display: inline-block; overflow: hidden; } - -.input-Select { background: none; border: 0; font-size: 0.9em; padding-right: 16px; width: 120%; -webkit-appearance: textarea; } - -.file-Input-Area { position: relative; } -.file-Input-Area label { cursor: default; } - -.input-File { cursor: pointer; left: 0; height: 100%; opacity: 0; position: absolute; top: 0; width: 100%; z-index: 9; } - -.input-File-Text { cursor: pointer; display: inline-block; } - -/* Inputs not supported in all browsers */ -.input-Color { border: 1px solid #cccccc; font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; margin-bottom: 8px; padding: 0 4px; height: 45px; width: 50%; } -.input-Color:focus { background: #f8f8f8; border: 1px solid #50B6D5; -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; box-shadow: 0 0 6px rgba(0, 0, 0, 0.25) inset; } - -.input-Number { font-size: 0.9em; } - -::-webkit-input-placeholder { /* WebKit browsers */ color: #5e7f8d; font-style: italic; } - -:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ color: #5e7f8d; font-style: italic; } - -::-moz-placeholder { /* Mozilla Firefox 19+ */ color: #5e7f8d; font-style: italic; } - -:-ms-input-placeholder { /* Internet Explorer 10+ */ color: #5e7f8d; font-style: italic; } - -.btn, .btn-Radio, .btn-Check, .btn-Small, .btn-Large, .btn-XLarge, .btn-Wide, .btn-Action, .btn-Action-2, .btn-Action-3, .btn-Neg, .btn-Neg:hover, .btn-Disable, .btn-Disable:hover, .btn-Disable:visited, .btn-Square, .btn-Round { background: #343434; -moz-border-radius: 18px; -webkit-border-radius: 18px; border-radius: 18px; background-clip: padding-box; -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25); -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.25); box-shadow: 0 0 6px rgba(0, 0, 0, 0.25); color: #f8f8f8; cursor: pointer; display: inline-block; font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 0.9em; line-height: 1; padding: 8px 20px; text-align: center; text-decoration: none !important; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4); -moz-transition: all 0.25s ease-in-out 0s; -ms-transition: all 0.25s ease-in-out 0s; -o-transition: all 0.25s ease-in-out 0s; -webkit-transition: all 0.25s ease-in-out 0s; transition: all 0.25s ease-in-out 0s; vertical-align: middle; -webkit-appearance: none; } -.btn.btn-Pad, .btn-Pad.btn-Radio, .btn-Pad.btn-Check, .btn-Pad.btn-Small, .btn-Pad.btn-Large, .btn-Pad.btn-XLarge, .btn-Pad.btn-Wide, .btn-Pad.btn-Action, .btn-Pad.btn-Action-2, .btn-Pad.btn-Action-3, .btn-Pad.btn-Neg, .btn-Pad.btn-Disable, .btn-Pad.btn-Square, .btn-Pad.btn-Round { padding: 8px 16px; } -.btn:hover, .btn:active, .btn-Radio:hover, .input-Radio.selected-Radio .btn-Radio, .btn-Check:hover, .input-Check.selected-Check .btn-Check, .btn-Small:hover, .btn-Large:hover, .btn-XLarge:hover, .btn-Wide:hover, .btn-Action:hover, .btn-Action-2:hover, .btn-Action-3:hover, .btn-Neg:hover, .btn-Disable:hover, .btn-Square:hover, .btn-Round:hover, .btn-Radio:active, .btn-Check:active, .btn-Small:active, .btn-Large:active, .btn-XLarge:active, .btn-Wide:active, .btn-Action:active, .btn-Action-2:active, .btn-Action-3:active, .btn-Neg:active, .btn-Disable:active, .btn-Square:active, .btn-Round:active { background: #131313; color: #f8f8f8; } -.btn:visited, .btn-Radio:visited, .btn-Check:visited, .btn-Small:visited, .btn-Large:visited, .btn-XLarge:visited, .btn-Wide:visited, .btn-Action:visited, .btn-Action-2:visited, .btn-Action-3:visited, .btn-Neg:visited, .btn-Disable:visited, .btn-Square:visited, .btn-Round:visited { color: #f8f8f8; } - -.btn-Small { font-size: 0.875em; padding: 4px 8px; } -.btn-Small.btn-Pad { padding: 4px 12px; } - -.btn-Large { font-size: 2em; padding: 12px; } -.btn-Large.btn-Pad { padding: 12px 20px; } - -.btn-XLarge { font-size: 2.5em; padding: 12px 16px; } -.btn-XLarge.btn-Pad { padding: 16px 24px; } - -.btn-Wide { display: block; } - -.btn-Action { background: #e93250; background: -webkit-linear-gradient(top, #e93250, #b11f36); background: -moz-linear-gradient(top, #e93250, #b11f36); background: -o-linear-gradient(top, #e93250, #b11f36); background: linear-gradient(top, #e93250, #b11f36); } -.btn-Action:hover { background: -webkit-linear-gradient(top, #d51c3a, #841526); background: -moz-linear-gradient(top, #d51c3a, #841526); background: -o-linear-gradient(top, #d51c3a, #841526); background: linear-gradient(top, #d51c3a, #841526); } - -.btn-Action-2 { background: #daeea5; } -.btn-Action-2:hover { background: #c0e265; } - -.btn-Action-3 { background: #f6f0e6; } -.btn-Action-3:hover { background: #e2ceae; } - -.btn-Neg, .btn-Neg:hover { background: #aaaaaa; color: #f8f8f8; } - -.btn-Disable, .btn-Disable:hover, .btn-Disable:visited { background: #fafafa; border: 1px solid #eee; color: #343434; cursor: default; } - -.btn-Square { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; background-clip: padding-box; } - -.btn-Round { -moz-border-radius: 12px; -webkit-border-radius: 12px; border-radius: 12px; background-clip: padding-box; } - -.nav ul, .nav ol { border: 1px solid #ccc; padding: 4px; } -.nav li { margin: 0; } -.nav a { display: block; padding: 4px 8px; text-decoration: underline; } -.nav a:hover { text-decoration: none; } - -.h-Nav > li, .bread > li { float: left; } - -.c-Nav { font-size: 0; text-align: center; } - -.c-Nav > li, .ah-Nav a { display: inline-block; font-size: 16px; } - -.v-Nav { /* really only needs styling applied to it, as vertical nav is default */ } - -.ah-Nav { font-size: 0; } - -.av-Nav .drop-Start { display: block; } - -.drop-Start { position: relative; } -.drop-Start:hover { background: #eee; } -.drop-Start:hover .drop-Menu { visibility: visible; } -.drop-Start a { white-space: nowrap; } - -.drop-Menu { background: #eee; border: 1px solid #ddd; position: absolute; visibility: hidden; z-index: 5; } - -.v-Nav .drop-Menu, .av-Nav .drop-Menu { left: 100%; top: 0; } - -.h-Nav .drop-Menu, .bread .drop-Menu, .ah-Nav .drop-Menu { left: 0; top: 100%; } - -.bread li:last-child:after { content: none; } -.bread li:after { content: ">"; display: inline-block; } -.bread a { display: inline-block; } -.bread span { display: inline-block; padding: 4px 8px; } - -.float-Left { float: left; } - -.float-Right { float: right; } - -.float-None { float: none; } - -.clear { clear: both; } - -.clearfix, .row, .c-Row, nav ul, nav ol, .media, .media-Reverse, .media-No-Wrap-Reverse, .media-No-Wrap { *zoom: 1; } -.clearfix:before, .row:before, .c-Row:before, nav ul:before, nav ol:before, .media:before, .media-Reverse:before, .media-No-Wrap-Reverse:before, .media-No-Wrap:before, .clearfix:after, .row:after, .c-Row:after, nav ul:after, nav ol:after, .media:after, .media-Reverse:after, .media-No-Wrap-Reverse:after, .media-No-Wrap:after { content: ""; display: table; } -.clearfix:after, .row:after, .c-Row:after, nav ul:after, nav ol:after, .media:after, .media-Reverse:after, .media-No-Wrap-Reverse:after, .media-No-Wrap:after { clear: both; } - -.overflow { overflow: hidden; } - -blockquote, h1, h2, h3, h4, h5, h6, li, p, small, code, kbd, pre, samp, dt, form, table { margin-bottom: 12px; } - -.d-Block, .show { display: block; } - -.d-None, .mobile-Show { display: none; } - -.hidden { display: none !important; visibility: hidden; } - -.invis { border: 0 !important; height: 1px !important; left: -999999px !important; overflow: hidden !important; position: absolute !important; width: 1px !important; } - -.pic, .pic-Rounded { border: 1px solid white; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-clip: padding-box; -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); display: inline-block; overflow: hidden; width: 90px; } -.pic img, .pic-Rounded img { display: block; } - -.pic-Rounded { -moz-border-radius: 100px; -webkit-border-radius: 100px; border-radius: 100px; background-clip: padding-box; } - -/* - Code Example - -------------------------- - - - -*/ -.media, .media-Reverse, .media-No-Wrap-Reverse, .media-No-Wrap { margin-bottom: 16px; position: relative; z-index: 2; } -.media .media-Text > :last-child, .media-Reverse .media-Text > :last-child, .media-No-Wrap-Reverse .media-Text > :last-child, .media-No-Wrap .media-Text > :last-child { margin-bottom: 0; } -.media .media-Object, .media-Reverse .media-Object, .media-No-Wrap-Reverse .media-Object, .media-No-Wrap .media-Object { float: left; margin: 0 16px 8px 0; max-width: 30%; } - -.media-Reverse .media-Object, .media-No-Wrap-Reverse .media-Object { float: right; margin: 0 0 8px 16px; max-width: 30%; } - -.media-No-Wrap { padding-left: 100px; } -.media-No-Wrap .media-Text { float: left; width: 100%; } -.media-No-Wrap .media-Object { margin-left: -100px; margin-right: 0; } - -.media-No-Wrap-Reverse { padding-right: 100px; } -.media-No-Wrap-Reverse .media-Object { margin-right: -100px; margin-left: 0; } - -/* - Code Example - -------------------------- -
-
- -
-
-

Title for Meida Element

-

Paragraph text to go along with media element.

-
-
- - -- Divs were used in this example, but the media element could be applied to various situations. - Ie. could be used for a header or footer area where a logo is placed on the right or left w/text to the side. -*/ -.emph-Block { background: #fafafa; border: 1px solid #ddd; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-clip: padding-box; -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); margin-bottom: 16px; min-height: 50px; padding: 16px; } -.emph-Block > :last-child { margin-bottom: 0; } - -.kill-List, .intro-List { list-style: none; padding: 0; } - -.numbered { list-style-type: decimal; } - -.shout-Out { background: #f8f8f8; border-left: 4px solid #343434; padding: 16px; } -.shout-Out:before, .shout-Out:after { display: none; } - -.action-Link:after { content: "\00A0" "\00BB"; } - -.txt-Center { text-align: center; } - -.txt-Left { text-align: left; } - -.txt-Right { text-align: right; } - -.txt-Just { text-align: justify; } - -.highlight, .slant { background: yellow; color: #131313; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; background-clip: padding-box; display: inline-block; padding: 4px; vertical-align: text-top; } - -.slant { -moz-transform: skewX(-16deg); -ms-transform: skewX(-16deg); -o-transform: skewX(-16deg); -webkit-transform: skewX(-16deg); transform: skewX(-16deg); } - -.text-Correct { display: inline-block; -moz-transform: skewX(16deg); -ms-transform: skewX(16deg); -o-transform: skewX(16deg); -webkit-transform: skewX(16deg); transform: skewX(16deg); } - -/* - Code Example: Slanted Text w/Highlight - -------------------------- - - Text Goes here - - - Code Example: Normal flowing text w/slanted highlight - -------------------------- - - Text Goes here - -*/ -.multi { -moz-column-count: 3; -webkit-column-count: 3; column-count: 3; -moz-column-gap: 40px; -webkit-column-gap: 40px; column-gap: 40px; -moz-column-rule: 1px outset #eeeeee; -webkit-column-rule: 1px outset #eeeeee; column-rule: 1px outset #eeeeee; } - -body { font-family: "Lucida Grande", verdana, "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 300; } - -h1, h2, h3, h4, h5, h6 { font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif; } - -.giga { font-size: 4.375em; font-weight: 700; } - -.ultra { font-size: 3.75em; font-weight: 600; } - -.mega { font-size: 3.5em; font-weight: 300; } - -h1, .alpha { font-size: 3.125em; font-weight: 400; } - -h2, .beta { font-size: 2.5em; font-weight: 400; } - -h3, .gamma { font-size: 2em; font-weight: 400; } - -h4, .delta { font-size: 1.625em; font-weight: 300; } - -h5, .eps { font-size: 1.375em; font-weight: 300; } - -h6, .zeta { font-size: 1.125em; font-weight: 300; } - -p, li, .base { font-size: 0.9em; font-weight: 300; } - -small, .tiny { font-size: 0.875em; font-weight: 300; } - -.mini { font-size: 0.75em; font-weight: 300; } - -.action-Link { float: right; } - -body { background: #CCDF9B; } - -::-moz-selection { background: #e93250; color: #eeeeee; text-shadow: none; } - -::selection { background: #e93250; color: #eeeeee; text-shadow: none; } - -.page { min-height: 600px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .page { min-height: 25px; } } - -.content, .footer-Content { margin: 0 auto; max-width: 1200px; padding: 20px; } -.content > :last-child, .footer-Content > :last-child { margin-bottom: 0; } - -a { color: #e93250; } -a:hover, a:active { color: #ba142f; } -a:visited { color: #A5424B; } - -:focus, a:focus, a:active, input[type="submit"]::-moz-focus-inner { outline: none; } - -@media (max-device-width: 480px), screen and (max-width: 800px) { .col-33, .col-40, .col-60 { float: none; width: 100%; } } - -@media (max-device-width: 480px), screen and (max-width: 800px) { .col-50 { float: none; padding: 0; width: 100% !important; } } - -.header-Base { background: #65a0ad; border-bottom: 6px solid #e93250; min-height: 42px; overflow: hidden; } - -@media (max-device-width: 480px), screen and (max-width: 800px) { .header-Content { padding: 0; } } - -.blurb { background: #fff; } - -.header-Intro { background: url(../img/neocity.jpg) 95% bottom no-repeat; min-height: 214px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .header-Intro { -moz-background-size: cover; -ms-background-size: cover; -o-background-size: cover; -webkit-background-size: cover; background-size: cover; min-height: 2px; } } - -.header-Outro { background: #30424b -moz-linear-gradient(top, #2b3c43 0%, #354751 100%); /* FF3.6+ */ background: #30424b -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b3c43), color-stop(100%, #354751)); /* Chrome,Safari4+ */ background: #30424b -webkit-linear-gradient(top, #2b3c43 0%, #354751 100%); /* Chrome10+,Safari5.1+ */ background: #30424b -o-linear-gradient(top, #2b3c43 0%, #354751 100%); /* Opera 11.10+ */ background: #30424b -ms-linear-gradient(top, #2b3c43 0%, #354751 100%); /* IE10+ */ background: #30424b linear-gradient(to bottom, #2b3c43 0%, #354751 100%); /* W3C */ -moz-box-shadow: inset 0 7px 10px 0 rgba(0, 0, 0, 0.1); -webkit-box-shadow: inset 0 7px 10px 0 rgba(0, 0, 0, 0.1); box-shadow: inset 0 7px 10px 0 rgba(0, 0, 0, 0.1); color: #fafafa; } - -.hp .header-Outro .col-50 { width: 48%; } - -.hp .header-Outro .signup-Area { float: right; } - -@media (max-device-width: 480px), screen and (max-width: 800px) { .intro-List { margin-bottom: 20px; padding: 20px !important; } } -.intro-List li { padding-left: 36px; padding-right: 12px; margin-bottom: 20px; } -.intro-List h2 { margin-bottom: 2px; } -.intro-List p { color: #B2BCC1; } - -.intro-Icon { background: url(../img/icons.png) no-repeat; display: block; height: 37px; left: -10px; position: absolute; top: 2px; width: 37px; } - -.intro-Tools { position: relative; } - -.intro-Question { position: relative; } -.intro-Question .intro-Icon { background-position: 0 -40px; } - -.intro-Social { position: relative; } -.intro-Social .intro-Icon { background-position: 0 -80px; } - -.signup-Area { min-height: 100px; position: relative; } - -.signup-Form { background: #354751; border-radius: 4px 4px 0 0; -moz-box-shadow: 1px 2px 12px 2px rgba(0, 0, 0, 0.15); -webkit-box-shadow: 1px 2px 12px 2px rgba(0, 0, 0, 0.15); box-shadow: 1px 2px 12px 2px rgba(0, 0, 0, 0.15); height: 600%; overflow: hidden; position: absolute; top: -45px; width: 95%; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .signup-Form { height: auto; margin: 0; overflow: visible; padding-bottom: 20px; position: static; width: auto; } } -.signup-Form h2 { margin-bottom: 0; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); font-size: 1.8em; } -.signup-Form hr { border-bottom: 1px solid #4a6677; border-top: 1px solid #1d282d; margin: 4px 0 22px; } -.signup-Form fieldset { background: url(../img/sign-up-bg.png) repeat-x center top; padding: 20px 33px; } -.signup-Form label { color: #81b8c6; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .signup-Form label { font-size: 70%; } } -.signup-Form .input-Area { background: #29383f; border: 0 solid black; -moz-box-shadow: inset 1px 3px 10px 0px rgba(0, 0, 0, 0.2); -webkit-box-shadow: inset 1px 3px 10px 0px rgba(0, 0, 0, 0.2); box-shadow: inset 1px 3px 10px 0px rgba(0, 0, 0, 0.2); color: #557380; margin-bottom: 28px; margin-right: 4px; padding: 11px 12px 9px 12px; width: 62%; } -.signup-Form .input-Area:focus { color: #eee; } -.signup-Form .btn-Action { padding: 10px 25px; } - -.small-Nav { background: #30424B; display: none; position: fixed; right: 0; top: 0; width: 50px; z-index: 9999; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .small-Nav { display: block; } } - -.header-Nav { background: #5e95a1; border-bottom: 1px solid #92B4BD; -moz-transition: all 0.35s; -ms-transition: all 0.35s; -o-transition: all 0.35s; -webkit-transition: all 0.35s; transition: all 0.35s; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .header-Nav { position: fixed; top: -900px !important; } } -.header-Nav.show-Nav { top: 0 !important; } -.header-Nav a, .header-Nav a:visited { color: #fff; padding: 8px 12px; text-decoration: none; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .header-Nav a, .header-Nav a:visited { display: block; } } -.header-Nav a:hover, .header-Nav a:visited:hover { background: #528995; text-decoration: underline; } -.header-Nav a.selected, .header-Nav a:active, .header-Nav a:visited.selected, .header-Nav a:visited:active { background: #528995; text-decoration: underline; } - -.constant-Nav { float: left; position: relative; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .constant-Nav { float: none; } - .constant-Nav li { float: none; } } - -.status-Nav { float: right; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .status-Nav { float: none; } } -.status-Nav li { float: left; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .status-Nav li { float: none; } } - -.hp .header-Base { padding-top: 46px; } - -.hp .header-Nav { left: 0; position: fixed; top: 0; width: 100%; z-index: 3; } - -.hp .hp-Logo { left: -90px; position: fixed; -moz-transition: all 0.35s; -ms-transition: all 0.35s; -o-transition: all 0.35s; -webkit-transition: all 0.35s; transition: all 0.35s; } -.hp .hp-Logo.in-View { left: 0 !important; z-index: 99; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .hp .hp-Logo.in-View { left: -90px !important; } } - -.hp .logo { padding-top: 45px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .hp .logo { padding-left: 20px; padding-right: 20px; } } - -.constant-Nav { margin-left: -88px; -moz-transition: all 0.35s; -ms-transition: all 0.35s; -o-transition: all 0.35s; -webkit-transition: all 0.35s; transition: all 0.35s; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .constant-Nav { margin-left: 0; } } -.constant-Nav.in-View { margin-left: 0; padding-left: 70px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .constant-Nav.in-View { padding-left: 0; } } - -.add-Stripe { border-bottom: 6px solid #E93250; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .add-Stripe { border: 0; } } - -.interior .page { padding-top: 22px; float: left; width: 100%; } - -.interior .signup-Form { top: 0; } - -.interior .header-Base { left: 0; overflow: visible; position: fixed; top: 0; width: 100%; z-index: 9; } - -.int-Logo { left: 0; position: absolute; top: 0; width: 70px; z-index: 9; } - -.interior .header-Nav { padding-left: 70px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .interior .header-Nav { width: 100%; } } - -.interior .constant-Nav { margin: 0; } - -.content-Base { background: #f6f0e6; min-height: 500px; padding-bottom: 50px; padding-top: 10px; } -.content-Base h1, .content-Base h2, .content-Base h3, .content-Base h4, .content-Base h5, .content-Base h6 { color: #e93250; } - -.content, .footer-Content, .footer-Content { padding: 20px 3%; } - -.single-Col { max-width: 800px; } - -.twitter-tweet.twitter-tweet-rendered { margin: 0 auto 30px !important; } - -.interior .header-Outro { padding-top: 30px; overflow: hidden; } - -.interior .header-Outro h1 { font-size: 2.5em; margin-top: 15px; } - -.site-url { font-size: 18px; margin-bottom: 8px; } - -.site-url a { color: #e93250; font-weight: bold; } - -.interior .header-Outro .subtitle { font-size: 1em; margin-top: -15px; } - -.content.wide, .wide.footer-Content { padding-left: 6%; padding-right: 6%; } - -.content.misc-page, .misc-page.footer-Content { background: #FAF6F1; -moz-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); -webkit-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); padding: 1px 3% 40px 3%; } - -.content.misc-page h3, .misc-page.footer-Content h3, .content.misc-page h4, .misc-page.footer-Content h4, .content.misc-page h5, .misc-page.footer-Content h5, .content.misc-page h6, .misc-page.footer-Content h6 { margin-top: 1em; } - -.content.misc-page h2, .misc-page.footer-Content h2 { font-size: 1.6em; margin-top: 1em; } - -.content.misc-page h3, .misc-page.footer-Content h3, .content.misc-page h4, .misc-page.footer-Content h4, .content.misc-page h5, .misc-page.footer-Content h5 { font-size: 1.2em; } - -.content.misc-page h6, .misc-page.footer-Content h6 { font-size: 1em; } - -.content.misc-page h1, .misc-page.footer-Content h1 { font-size: 2.2em; margin-bottom: 20px; } - -.content.misc-page hr, .misc-page.footer-Content hr { margin: 35px 0 25px 0; } - -.interior .header-Outro .col.col-50.signup-Area { padding-left: 0; } - -.interior .header-Outro a { color: #E93250; } - -.interior .header-Outro .btn-Action { color: #fff; } - -.interior .signup-Area { min-height: 164px; } - -.interior .signup-Area img { -moz-box-shadow: 1px 2px 5px 2px rgba(0, 0, 0, 0.1); -webkit-box-shadow: 1px 2px 5px 2px rgba(0, 0, 0, 0.1); box-shadow: 1px 2px 5px 2px rgba(0, 0, 0, 0.1); border: 4px solid white; } - -.interior .signup-Area.large img { border: 6px solid white; } - -.interior .header-Outro .screenshot { width: 235px; height: 141px; } - -.interior .header-Outro ul { margin: 0; padding: 0; } - -.interior .header-Outro ul li { list-style: none; font-size: 80%; margin-bottom: 2px; } - -.interior .signup-Form fieldset { padding: 20px; } - -.interior .header-Outro h2 { margin-top: 12px; font-size: 1.8em; } - -.welcome { background: #daeea5 url(../img/heartcat.png) no-repeat 20px center; background-size: 77px 81px; padding: 20px 20px 20px 108px; margin-bottom: 30px; } - -.welcome h4 { font-size: 1.2em; margin-bottom: 4px; } - -.welcome .close-button { float: right; background: url(../img/welcome-close.png) no-repeat; width: 19px; height: 19px; } - -.files { float: left; background: #E4D8CB; width: 100%; position: relative; } - -.files .header { background: #5E95A1; color: #fff; float: left; width: 100%; padding: 10px 20px; } - -.files .breadcrumbs { float: left; font-weight: bold; margin-top: 4px; } - -.files .actions { float: right; } - -.files .btn-Action { margin-left: 8px; } - -.btn-Action span { background-repeat: no-repeat; } - -.btn-Action.new-Page span { background-image: url(../img/new-page.png); background-position-y: 1px; padding-left: 29px; } - -.btn-Action.new-Folder span { background-image: url(../img/new-folder.png); padding-left: 26px; } - -.btn-Action.upload span { background-image: url(../img/upload.png); padding-left: 24px; } - -.files .list { padding: 20px; } - -.files .list .upload-Boundary { float: left; border: 3px dashed #F6F0E6; width: 100%; margin: 18px 0; padding: 10px; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; min-height: 300px; } - -.files .list .upload-Boundary.with-instruction { background: url(../img/drag-drop.png) no-repeat center center; } - -.files .uploading-overlay { width: 100%; height: 100%; position: absolute; background-color: rgba(0, 0, 0, 0.35); z-index: 100; } - -.files .uploading { background: #fff; font-style: italic; margin-left: auto; margin-right: auto; width: 400px; margin-top: 14%; padding: 25px 40px 28px 40px; -webkit-box-shadow: 1px 1px 21px 5px rgba(50, 50, 50, 0.5); -moz-box-shadow: 1px 1px 21px 5px rgba(50, 50, 50, 0.5); box-shadow: 1px 1px 21px 5px rgba(50, 50, 50, 0.5); border-radius: 10px; } - -.files .uploading p { margin-bottom: 2px; } - -.files .progress-bar { background: #CCCCCC; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; width: 100%; position: relative; margin-top: 14px; height: 10px; overflow: hidden; } - -.files .progress-bar .progress { background: #E93250; height: 100%; -webkit-border-top-right-radius: 0px; -webkit-border-bottom-right-radius: 0px; -moz-border-radius-topright: 0px; -moz-border-radius-bottomright: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; -webkit-border-top-left-radius: 20px; -webkit-border-bottom-left-radius: 20px; -moz-border-radius-topleft: 20px; -moz-border-radius-bottomleft: 20px; border-top-left-radius: 20px; border-bottom-left-radius: 20px; position: relative; overflow: hidden; display: block; } - -.file { float: left; padding: 5px 0px; margin-right: 10px; margin-bottom: 10px; width: 125px; text-align: center; display: block; position: relative; } - -.file .title { font-weight: bold; font-size: 12px; color: #666; margin-top: 4px; text-decoration: none; white-space: nowrap; overflow: hidden; display: block; } - -.html-thumbnail { font-size: 11px; margin-top: 5px; margin-left: 10px; display: block; position: relative; width: 105px; height: 63px; } - -.html-thumbnail.html img { width: 105px; height: 63px; -webkit-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); -moz-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); } - -.html-thumbnail.image img { max-width: 105px; max-height: 63px; width: auto; height: auto; } - -.html-thumbnail.misc { width: 63px; height: 63px; margin-left: auto; margin-right: auto; } - -.misc-icon { background: url(../img/misc-file.png) no-repeat 0px 0px; width: 67px; height: 67px; display: block; padding-top: 35px; font-size: 14px; color: #bbb; font-weight: bold; margin-left: auto; margin-right: auto; } - -.overlay a { color: white; text-decoration: none; font-size: 14px; display: block; } - -.overlay i { font-weight: bold; } - -.overlay { position: absolute; top: 0; width: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: none; } - -.link-overlay { display: block; position: absolute; bottom: 0px; left: 0px; width: 123px; height: 30px; } - -.file > .overlay { -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; padding-top: 15px; background-color: rgba(0, 0, 0, 0.15); } - -.filehover:hover .overlay, .fileimagehover:hover .overlay { display: block; } - -.html-thumbnail.misc.fileimagehover .overlay { margin: 1px 0 0 2px; } - -.site-actions { float: left; margin-top: 20px; font-size: 90%; } - -.site-actions a { color: #666; } - -@media (min-width: 1200px) { .container { width: 1200px; } } -@media (min-width: 700px) and (max-width: 1200px) { .container { width: 100%; } } -.content.misc-page.columns, .misc-page.columns.footer-Content { float: left; padding: 0; position: relative; clear: both; width: 100%; overflow: hidden; } - -.col-left { float: left; width: 100%; position: relative; border-right: 1px solid #ddd; } - -.right-col { background: #FAF6F1; } - -.content.misc-page .col-33 h3, .misc-page.footer-Content .col-33 h3 { font-size: 1.5em; } - -.content.misc-page .col-33 h3:nth-of-type(1), .misc-page.footer-Content .col-33 h3:nth-of-type(1) { margin-top: 0; } - -.right-col .col-left { background-color: white; right: 33%; } - -.content.misc-page.columns .col, .misc-page.columns.footer-Content .col { padding: 25px 30px 30px 30px; position: relative; overflow: hidden; } - -.content.misc-page.columns .col-66, .misc-page.columns.footer-Content .col-66 { width: 67%; left: 33%; } - -.content.misc-page.columns .col-33, .misc-page.columns.footer-Content .col-33 { width: 33%; left: 33%; } - -.interior .header-Outro.with-columns { padding-top: 22px; } - -.interior .header-Outro.with-columns h3 { float: left; margin-bottom: 0; font-size: 1.7em; } - -.interior .header-Outro.with-columns .content, .interior .header-Outro.with-columns .footer-Content { padding: 0; } - -.interior .header-Outro.with-columns .col { padding: 25px 0 8px 30px; } - -.interior .header-Outro.with-columns .col-32 { width: 33%; } - -.interior .header-Outro.with-columns .col-66 { width: 67%; border-right: 1px solid #0B0F11; } - -.interior .header-Outro.with-columns .col-32 .edit { margin-top: 4px; float: right; margin-right: 30px; color: #fff; } - -.feed-filter { float: left; margin-top: 1.4em; font-size: 0.8em; margin-left: 1.5em; } - -.interior .header-Outro .feed-filter a { color: white; margin-left: 13px; } - -.site-suggestion { float: left; width: 156px; margin-right: 20px; margin-bottom: 20px; height: 160px; } - -.stats { margin-bottom: 2em; float: left; width: 100%; } - -.content.misc-page.columns .stats .col, .misc-page.columns.footer-Content .stats .col { padding: 0; margin-bottom: 2em; } - -.large-portrait { border: 10px solid white; -moz-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); } - -.site-portrait { border: 5px solid white; -moz-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.2); float: left; margin-bottom: .5em; } - -.site-portrait img { width: 146px; } - -.site-portrait .caption { display: block; clear: both; font-size: .8em; margin-top: .2em; margin-bottom: -.2em; } - -a.tag { font-size: .7em; text-transform: uppercase; background: #FFFFCC; color: #C1A009; float: left; padding: 1px 5px; -moz-box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2); box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2); margin-right: 3px; margin-right: 10px; margin-bottom: 7px; } - -a.tag:hover { text-decoration: none; background: #FFEE8A; } - -.following { width: 100%; float: left; margin-bottom: 2em; } - -.avatar { height: 37px; width: 37px; margin: 0 4px 4px 0; float: left; } - -.news-item { width: 100%; float: left; } - -.news-item.update, .news-item.tags, .news-item.comment.for-me, .news-item.tip.for-me { margin: 1.8em 0; } - -.news-item:nth-of-type(1) { margin-top: 0; } - -.news-item.first { margin-top: 0; } - -.news-item .user { font-weight: bold; color: #000; } - -.news-item .comment { color: #000; font-style: normal; font-size: .8em; } - -.news-item.for-me .comment { color: #000; font-style: normal; margin-top: .2em; font-size: .9em; margin-bottom: -.4em; } - -.news-item .icon { width: 20px; height: 20px; background: #E6E6E6; float: left; margin-right: 12px; } - -.news-item.comment .icon { background: #DAEEA5; } - -.news-item.comment.for-me .icon, .news-item.tip.for-me .icon { background-size: 62px 62px; width: 82px; height: 62px; background-position: right top; background-repeat: no-repeat; } - -.news-item.update .icon { background: #E93250; } - -.news-item.tip .icon { background: #FFCC00; } - -.news-item.follow .icon { background: #3399CC; } - -.news-item .title { margin-bottom: .4em; position: relative; width: 100%; float: left; } - -.news-item .date { float: right; color: #aaa; font-size: 11px; margin-top: .5em; } - -.news-item .files { background: #eee; padding: 8px 10px 2px 10px; border-top: 1px solid #ddd; margin-bottom: 4px; } - -.news-item .file { width: 90px; margin-bottom: 0; padding: 0; } - -.news-item .file a:hover { text-decoration: none; } - -.news-item .html-thumbnail { margin-top: 1px; margin-left: 0px; width: auto; } - -.news-item .site-suggestion { height: auto; margin-bottom: 10px; } - -.news-item .tag { float: none; margin-left: 4px; } - -.news-item .actions { font-size: 11px; } - -.news-item .actions a { margin-right: 6px; } - -.news-item .content, .news-item .footer-Content { padding: 0 0 0 32px; } - -.news-item .comments { margin-bottom: 1.5em; margin-top: .7em; } - -.news-item .comments .comment { font-size: .8em; clear: both; } - -.news-item .comments .comment .user { margin-right: 5px; } - -.news-item .comments .comment .actions { margin-top: .3em; } - -.news-item .avatar { margin-right: 10px; } - -.signup-Area.large { width: 418px; height: 236px; } - -.interior .header-Outro.with-site-image { padding-top: 20px; } - -.report { margin-top: 2em; float: left; width: 100%; font-size: .8em; } - -.report, .report a { color: #999; } - -.interior .header-Outro .actions a { margin-right: 6px; } - -.interior .header-Outro .stats { margin-bottom: 1.2em; float: left; width: 100%; margin-top: 2em; } - -.interior .header-Outro .stats strong { font-size: 1.5em; font-weight: bold; color: #DAEEA5; } - -.interior .header-Outro .stats span { font-size: .7em; text-transform: uppercase; clear: both; display: block; } - -.interior .header-Outro .stats .stat { float: left; width: 90px; text-align: center; } - -.interior .header-Outro .stats .stat.tips { width: 60px; } - -.btn-Action.follow span { background-image: url(../img/follow.png); padding-left: 22px; } - -.btn-Action.tip span { background-image: url(../img/tip.png); padding-left: 26px; background-position-y: -1px; } - -.btn-Action.share span { background-image: url(../img/share.png); padding-left: 26px; } - -.col-33 .stats .stat { margin-bottom: .4em; } - -.col-33 .stats .stat span { width: 10em; float: left; } - -.archives { float: left; width: 100%; margin-bottom: 2em; } - -.archives img { float: left; border: 3px solid white; -webkit-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); -moz-box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.3); width: 72px; } - -.archives img { margin-right: 10px; } - -.more { float: left; clear: both; margin-top: 1em; font-size: .8em; } - -.post-comment { background: #DAEEA5; float: left; width: 111%; padding: 20px 0 10px 30px; margin: -25px 0 28px -30px; } - -.post-comment input { width: 84%; float: left; } - -.post-comment .btn-Action { margin-left: 10px; margin-top: 4px; } - -.supporter-badge { background: url(../img/supporter.png); width: 99px; height: 26px; float: left; margin-top: 7px; margin-left: 10px; } - -.title-with-badge { float: left; width: 100%; } - -.title-with-badge span { float: left; } - -.section.featured-Websites { background: #65A0AD; } - -.section.featured-Websites h2 { color: white; } - -.section { padding: 50px 10%; margin: 0 auto; } - -.section.previews { background: #f6f0e6; } - -.section.previews h2, .section.previews p { color: #31424B; } - -.section h2 { font-size: 1.6em; } - -.section.last { background: #666666; } - -.section.last h2, .section.last blockquote { color: white; } - -.selected { font-weight: bold; } - -.modal-body { max-height: 800px; overflow-y: visible; } - -.tt-dropdown-menu { padding: 0px 10px 0px 10px; background: #FFFFFF; } - -.footer-Base { color: #5e5b56; float: left; width: 100%; } -.footer-Base h1, .footer-Base h2, .footer-Base h3, .footer-Base h4 { color: #8b9a7a; } - -.footer-Intro { background: #daeea5; border-top: 1px solid #cedbab; -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); position: relative; } - -.footer-Content { padding-top: 20px; padding-bottom: 20px; padding-left: 40px; padding-right: 20px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .footer-Content { padding-left: 20px; } } - -.footer-Content .row, .footer-Content .c-Row { margin-left: 0; } - -.f-Col { -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; min-height: 125px; padding-bottom: 28px; position: relative; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .f-Col { min-height: 2px; padding-left: 15px !important; padding-right: 100px !important; } } -.f-Col .action-Link { bottom: 0; position: absolute; right: 12px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .f-Col .action-Link { position: static; } } - -.footer-icon { background-repeat: no-repeat; position: absolute; right: 0; top: -70px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .footer-icon { -moz-background-size: 100%; -ms-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; height: 90px !important; top: 0; width: 77px !important; } } - -.f-Col-1 { padding-right: 12px; } -.f-Col-1 .footer-icon { background-image: url(../img/support-us.png); height: 104px; right: 5px; width: 92px; } - -.f-Col-2 { padding-left: 15px; padding-right: 6px; position: relative; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .f-Col-2 { border-bottom: 1px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, 0.1); margin: 20px 0; overflow: hidden; padding: 20px 0; } - .f-Col-2 .footer-icon { top: 14px; } } -.f-Col-2:before, .f-Col-2:after { background: url("../img/border.png") no-repeat 0 -20px; content: ""; height: 200px; opacity: 0.2; position: absolute; top: 0; width: 1px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .f-Col-2:before, .f-Col-2:after { display: none; } } -.f-Col-2:before { left: -14px; } -.f-Col-2:after { right: -14px; } -.f-Col-2 .footer-icon { background-image: url(../img/about-neocities.png); width: 100px; height: 106px; } - -.f-Col-3 { padding-left: 20px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .f-Col-3 { padding-left: 0; } } -.f-Col-3 .footer-icon { background-image: url(../img/latest-news.png); height: 103px; width: 134px; } - -.footer-Outro { background: #ccdf9b; border-top: 1px solid #b3c388; overflow: hidden; } -.footer-Outro a { color: #5e5b56; } - -.credits { margin-bottom: 0; } - -.footer-Nav { text-transform: uppercase; } -.footer-Nav .h-Nav, .footer-Nav .bread { float: right; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .footer-Nav .h-Nav, .footer-Nav .bread { float: none; margin-top: 15px; } - .footer-Nav .h-Nav li:first-child a, .footer-Nav .bread li:first-child a { padding-left: 0; } } -.footer-Nav .h-Nav a, .footer-Nav .bread a { padding: 0 8px; } -.footer-Nav .h-Nav li:last-child a, .footer-Nav .bread li:last-child a { padding-right: 0; } - -.alert { background-color: #F5BA00; color: #fff; } - -.website-Gallery { list-style: none; padding: 10px 0; } -.website-Gallery li { float: left; margin-bottom: 8px; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .website-Gallery li { width: 50%; } } -.website-Gallery a { padding: 0 8px; display: block; } -.website-Gallery.int-Gall li { border: 1px solid #ccc; margin: 0 .5% 12px; width: 24%; } -@media (max-device-width: 480px), screen and (max-width: 800px) { .website-Gallery.int-Gall li { width: 49%; } } -.website-Gallery.int-Gall li a { padding: 8px; } - -.neo-SS, .neo-Screen-Shot { background: #fff; -moz-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); box-shadow: 0 8px 8px -8px rgba(0, 0, 0, 0.2); display: block; height: auto !important; padding: 8px; position: relative; width: 100%; } - -.img-Holder { -moz-background-size: cover !important; -webkit-background-size: cover !important; background-size: cover !important; display: block; } - -.hp-Gallery img, .neo-Screen-Shot img { width: 100%; } diff --git a/tests/acceptance/dashboard_tests.rb b/tests/acceptance/dashboard_tests.rb new file mode 100644 index 00000000..cf4b60a6 --- /dev/null +++ b/tests/acceptance/dashboard_tests.rb @@ -0,0 +1,26 @@ +require_relative './environment.rb' + +describe 'dashboard' do + describe 'create directory' do + + describe 'logged in' do + + include Capybara::DSL + + before do + Capybara.reset_sessions! + @site = Fabricate :site + page.set_rack_session id: @site.id + end + + it 'creates a base directory' do + visit '/dashboard' + click_link 'New Folder' + fill_in 'name', with: 'testimages' + click_button 'Create' + page.must_have_content /testimages/ + File.directory?(@site.files_path('testimages')).must_equal true + end + end + end +end \ No newline at end of file diff --git a/tests/acceptance/environment.rb b/tests/acceptance/environment.rb new file mode 100644 index 00000000..b0f3379b --- /dev/null +++ b/tests/acceptance/environment.rb @@ -0,0 +1,7 @@ +require_relative '../environment' + +Capybara.app = Sinatra::Application + +def teardown + Capybara.reset_sessions! +end \ No newline at end of file diff --git a/tests/acceptance/index_tests.rb b/tests/acceptance/index_tests.rb new file mode 100644 index 00000000..70b8a7ac --- /dev/null +++ b/tests/acceptance/index_tests.rb @@ -0,0 +1,11 @@ +require_relative './environment.rb' + +describe 'index' do + include Capybara::DSL + it 'goes to signup' do + Capybara.reset_sessions! + visit '/' + click_button 'Create My Website' + page.must_have_content('Create a New Website') + end +end \ No newline at end of file diff --git a/tests/acceptance/settings_tests.rb b/tests/acceptance/settings_tests.rb new file mode 100644 index 00000000..a425ee74 --- /dev/null +++ b/tests/acceptance/settings_tests.rb @@ -0,0 +1,44 @@ +require_relative './environment.rb' + +describe 'site/settings' do + describe 'change username' do + include Capybara::DSL + + def visit_signup + visit '/' + click_button 'Create My Website' + end + + def fill_in_valid + @site = Fabricate.attributes_for(:site) + fill_in 'username', with: @site[:username] + fill_in 'password', with: @site[:password] + fill_in 'email', with: @site[:email] + end + + before do + Capybara.reset_sessions! + visit_signup + end + + it 'does not allow bad usernames' do + visit '/' + click_button 'Create My Website' + fill_in_valid + click_button 'Create Home Page' + visit '/settings' + fill_in 'name', with: '' + click_button 'Change Name' + fill_in 'name', with: '../hack' + click_button 'Change Name' + fill_in 'name', with: 'derp../hack' + click_button 'Change Name' + ## TODO fix this without screwing up legacy sites + #fill_in 'name', with: '-' + #click_button 'Change Name' + page.must_have_content /valid.+name.+required/i + Site[username: @site[:username]].wont_equal nil + Site[username: ''].must_equal nil + end + end +end \ No newline at end of file diff --git a/tests/acceptance/signin_tests.rb b/tests/acceptance/signin_tests.rb new file mode 100644 index 00000000..78e13e46 --- /dev/null +++ b/tests/acceptance/signin_tests.rb @@ -0,0 +1,53 @@ +require_relative './environment.rb' + +describe 'signin' do + include Capybara::DSL + + def fill_in_valid + @site = Fabricate.attributes_for :site + fill_in 'username', with: @site[:username] + fill_in 'password', with: @site[:password] + end + + def fill_in_valid_signup + fill_in_valid + fill_in 'email', with: @site[:email] + end + + before do + Capybara.reset_sessions! + end + + it 'fails for invalid login' do + visit '/' + click_link 'Sign In' + page.must_have_content 'Welcome Back' + fill_in_valid + click_button 'Sign In' + page.must_have_content 'Invalid login' + end + + it 'fails for missing login' do + visit '/' + click_link 'Sign In' + auth = {username: SecureRandom.hex, password: Faker::Internet.password} + fill_in 'username', with: auth[:username] + fill_in 'password', with: auth[:password] + click_button 'Sign In' + page.must_have_content 'Invalid login' + end + + it 'logs in with proper credentials' do + visit '/' + click_button 'Create My Website' + fill_in_valid_signup + click_button 'Create Home Page' + Capybara.reset_sessions! + visit '/' + click_link 'Sign In' + fill_in 'username', with: @site[:username] + fill_in 'password', with: @site[:password] + click_button 'Sign In' + page.must_have_content 'Your Feed' + end +end \ No newline at end of file diff --git a/tests/acceptance_tests.rb b/tests/acceptance/signup_tests.rb similarity index 63% rename from tests/acceptance_tests.rb rename to tests/acceptance/signup_tests.rb index 2f58fe01..61ab5db5 100644 --- a/tests/acceptance_tests.rb +++ b/tests/acceptance/signup_tests.rb @@ -1,61 +1,4 @@ -require_relative './environment' - -Capybara.app = Sinatra::Application - -def teardown - Capybara.reset_sessions! - Capybara.use_default_driver -end - -describe 'index' do - include Capybara::DSL - it 'goes to signup' do - visit '/' - click_button 'Create My Website' - page.must_have_content('Create a New Website') - end -end - -describe 'change username' do - include Capybara::DSL - - def visit_signup - visit '/' - click_button 'Create My Website' - end - - def fill_in_valid - @site = Fabricate.attributes_for(:site) - fill_in 'username', with: @site[:username] - fill_in 'password', with: @site[:password] - fill_in 'email', with: @site[:email] - end - - before do - Capybara.reset_sessions! - visit_signup - end - - it 'does not allow bad usernames' do - visit '/' - click_button 'Create My Website' - fill_in_valid - click_button 'Create Home Page' - visit '/settings' - fill_in 'name', with: '' - click_button 'Change Name' - fill_in 'name', with: '../hack' - click_button 'Change Name' - fill_in 'name', with: 'derp../hack' - click_button 'Change Name' - ## TODO fix this without screwing up legacy sites - #fill_in 'name', with: '-' - #click_button 'Change Name' - page.must_have_content /valid.+name.+required/i - Site[username: @site[:username]].wont_equal nil - Site[username: ''].must_equal nil - end -end +require_relative './environment.rb' describe 'signup' do include Capybara::DSL @@ -215,56 +158,4 @@ describe 'signup' do page.must_have_content 'Your Feed' Site.last.tags.collect {|t| t.name}.must_equal ['derpie', 'shoujo'] end -end - -describe 'signin' do - include Capybara::DSL - - def fill_in_valid - @site = Fabricate.attributes_for :site - fill_in 'username', with: @site[:username] - fill_in 'password', with: @site[:password] - end - - def fill_in_valid_signup - fill_in_valid - fill_in 'email', with: @site[:email] - end - - before do - Capybara.reset_sessions! - end - - it 'fails for invalid login' do - visit '/' - click_link 'Sign In' - page.must_have_content 'Welcome Back' - fill_in_valid - click_button 'Sign In' - page.must_have_content 'Invalid login' - end - - it 'fails for missing login' do - visit '/' - click_link 'Sign In' - auth = {username: SecureRandom.hex, password: Faker::Internet.password} - fill_in 'username', with: auth[:username] - fill_in 'password', with: auth[:password] - click_button 'Sign In' - page.must_have_content 'Invalid login' - end - - it 'logs in with proper credentials' do - visit '/' - click_button 'Create My Website' - fill_in_valid_signup - click_button 'Create Home Page' - Capybara.reset_sessions! - visit '/' - click_link 'Sign In' - fill_in 'username', with: @site[:username] - fill_in 'password', with: @site[:password] - click_button 'Sign In' - page.must_have_content 'Your Feed' - end -end +end \ No newline at end of file diff --git a/tests/api_tests.rb b/tests/api_tests.rb index a6bb491a..07966838 100644 --- a/tests/api_tests.rb +++ b/tests/api_tests.rb @@ -84,17 +84,17 @@ describe 'api delete' do res[:error_type].must_equal 'cannot_delete_index' end - it 'fails with bad filename' do + it 'succeeds with weird filenames' do create_site basic_authorize @user, @pass @site.store_file 't$st.jpg', Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') post '/api/delete', filenames: ['t$st.jpg'] - res[:error_type].must_equal 'bad_filename' + res[:result].must_equal 'success' create_site basic_authorize @user, @pass post '/api/delete', filenames: ['./config.yml'] - res[:error_type].must_equal 'bad_filename' + res[:error_type].must_equal 'missing_files' end it 'fails with missing files' do @@ -137,13 +137,59 @@ describe 'api upload' do res[:error_type].must_equal 'missing_files' end - it 'fails for invalid filenames' do + it 'resists directory traversal attack' do create_site basic_authorize @user, @pass post '/api/upload', { '../lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') } - res[:error_type].must_equal 'bad_filename' + res[:result].must_equal 'success' + File.exist?(File.join(Site::SITE_FILES_ROOT, @site.username, 'lol.jpg')).must_equal true + end + + it 'scrubs root path slash' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + '/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:result].must_equal 'success' + File.exist?(File.join(Site::SITE_FILES_ROOT, @site.username, 'lol.jpg')).must_equal true + end + + it 'fails for missing file name' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + '/' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:error_type].must_equal 'invalid_file_type' + + create_site + basic_authorize @user, @pass + post '/api/upload', { + '' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:error_type].must_equal 'missing_files' + end + + it 'fails for file with no extension' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + 'derpie' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:error_type].must_equal 'invalid_file_type' + end + + it 'creates path for file uploads' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + 'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:result].must_equal 'success' + File.exist?(@site.files_path('derpie/derpingtons/lol.jpg')).must_equal true end it 'fails for invalid files' do @@ -180,7 +226,7 @@ describe 'api upload' do end def site_file_exists?(file) - File.exist?(@site.file_path('test.jpg')) + File.exist?(@site.files_path('test.jpg')) end def res diff --git a/tests/environment.rb b/tests/environment.rb index 0ad4794a..b6662f43 100644 --- a/tests/environment.rb +++ b/tests/environment.rb @@ -9,18 +9,26 @@ end SimpleCov.command_name 'minitest' +require 'rack_session_access' require './environment' -require 'webmock' -include WebMock::API require './app' Bundler.require :test #require 'minitest/pride' require 'minitest/autorun' - +require 'webmock' +include WebMock::API +require 'webmock/minitest' require 'sidekiq/testing' +Sinatra::Application.configure do |app| + app.use RackSessionAccess::Middleware +end + +require 'capybara/poltergeist' +require 'rack_session_access/capybara' + Site.bcrypt_cost = BCrypt::Engine::MIN_COST MiniTest::Reporters.use! MiniTest::Reporters::SpecReporter.new diff --git a/tests/files/index.html b/tests/files/index.html new file mode 100644 index 00000000..c6b500bd --- /dev/null +++ b/tests/files/index.html @@ -0,0 +1,8 @@ + + + Hello? + + +

Hi there!

+ + diff --git a/tests/site_file_tests.rb b/tests/site_file_tests.rb new file mode 100644 index 00000000..520c3639 --- /dev/null +++ b/tests/site_file_tests.rb @@ -0,0 +1,80 @@ +require_relative './environment.rb' +require 'rack/test' + +include Rack::Test::Methods + +def app + Sinatra::Application +end + +describe 'site_files' do + describe 'upload' do + + it 'succeeds with index.html file' do + site = Fabricate :site + PurgeCacheWorker.jobs.clear + ScreenshotWorker.jobs.clear + + post '/site_files/upload', { + 'files[]' => Rack::Test::UploadedFile.new('./tests/files/index.html', 'text/html'), + 'csrf_token' => 'abcd' + }, {'rack.session' => { 'id' => site.id, '_csrf_token' => 'abcd' }} + last_response.body.must_match /successfully uploaded/i + File.exists?(site.files_path('index.html')).must_equal true + + args = ScreenshotWorker.jobs.first['args'] + args.first.must_equal site.username + args.last.must_equal 'index.html' + end + + it 'succeeds with valid file' do + site = Fabricate :site + PurgeCacheWorker.jobs.clear + ThumbnailWorker.jobs.clear + post '/site_files/upload', { + 'files[]' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'), + 'csrf_token' => 'abcd' + }, {'rack.session' => { 'id' => site.id, '_csrf_token' => 'abcd' }} + last_response.body.must_match /successfully uploaded/i + File.exists?(site.files_path('test.jpg')).must_equal true + + queue_args = PurgeCacheWorker.jobs.first['args'].first + queue_args['site'].must_equal site.username + queue_args['path'].must_equal '/test.jpg' + + ThumbnailWorker.jobs.length.must_equal 1 + ThumbnailWorker.drain + + Site::THUMBNAIL_RESOLUTIONS.each do |resolution| + File.exists?(site.thumbnail_path('test.jpg', resolution)).must_equal true + end + end + + it 'works with directory path' do + site = Fabricate :site + ThumbnailWorker.jobs.clear + PurgeCacheWorker.jobs.clear + post '/site_files/upload', { + 'dir' => 'derpie/derptest', + 'files[]' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'), + 'csrf_token' => 'abcd' + }, {'rack.session' => { 'id' => site.id, '_csrf_token' => 'abcd' }} + last_response.body.must_match /successfully uploaded/i + File.exists?(site.files_path('derpie/derptest/test.jpg')).must_equal true + + PurgeCacheWorker.jobs.length.must_equal 1 + queue_args = PurgeCacheWorker.jobs.first['args'].first + queue_args['path'].must_equal '/derpie/derptest/test.jpg' + + ThumbnailWorker.jobs.length.must_equal 1 + ThumbnailWorker.drain + + Site::THUMBNAIL_RESOLUTIONS.each do |resolution| + File.exists?(site.thumbnail_path('derpie/derptest/test.jpg', resolution)).must_equal true + site.thumbnail_url('derpie/derptest/test.jpg', resolution).must_equal( + File.join "#{Site::THUMBNAILS_URL_ROOT}", site.username, "/derpie/derptest/test.jpg.#{resolution}.jpg" + ) + end + end + end +end \ No newline at end of file diff --git a/tests/workers/screenshot_worker_tests.rb b/tests/workers/screenshot_worker_tests.rb new file mode 100644 index 00000000..550bf8be --- /dev/null +++ b/tests/workers/screenshot_worker_tests.rb @@ -0,0 +1,28 @@ +require_relative '../environment.rb' + +describe ScreenshotWorker do + + it 'saves a screenshot for a root html file' do + worker = ScreenshotWorker.new + worker.perform 'kyledrake', 'index.html' + site = Fabricate :site + Site::SCREENSHOT_RESOLUTIONS.each do |r| + File.exists?(File.join(Site::SCREENSHOTS_ROOT, 'kyledrake', "index.html.#{r}.jpg")).must_equal true + site.screenshot_url('index.html', r).must_equal( + File.join(Site::SCREENSHOTS_URL_ROOT, site.username, "index.html.#{r}.jpg") + ) + end + end + + it 'saves a screenshot for a path html file' do + worker = ScreenshotWorker.new + worker.perform 'kyledrake', 'derpie/derp/index.html' + site = Fabricate :site + Site::SCREENSHOT_RESOLUTIONS.each do |r| + File.exists?(File.join(Site::SCREENSHOTS_ROOT, 'kyledrake', "derpie/derp/index.html.#{r}.jpg")).must_equal true + site.screenshot_url('derpie/derp/index.html', r).must_equal( + File.join(Site::SCREENSHOTS_URL_ROOT, site.username, "derpie/derp/index.html.#{r}.jpg") + ) + end + end +end \ No newline at end of file diff --git a/views/_header.erb b/views/_header.erb index 35e74557..060288e8 100644 --- a/views/_header.erb +++ b/views/_header.erb @@ -34,7 +34,7 @@ <% else %>
  • - +
  • diff --git a/views/_news.erb b/views/_news.erb index 9ffc8108..9a32cded 100644 --- a/views/_news.erb +++ b/views/_news.erb @@ -62,7 +62,7 @@ <%= File.extname(f).sub('.', '') %> <% end %> - <%= f %> + <%= f %> diff --git a/views/browse.erb b/views/browse.erb index ca509d43..7cc6dd4a 100644 --- a/views/browse.erb +++ b/views/browse.erb @@ -73,7 +73,10 @@
    - <%= site.username %> + + + <%= site.username %> +
    diff --git a/views/dashboard.erb b/views/dashboard.erb index 50df31bc..93dfb48a 100644 --- a/views/dashboard.erb +++ b/views/dashboard.erb @@ -15,14 +15,6 @@ display: none; } -
    @@ -88,10 +80,28 @@
    - +
    @@ -99,47 +109,49 @@
    -
    - <% current_site.file_list.each do |file| %> +
    + <% @file_list.each do |file| %>
    - <% if file.ext.match(Site::HTML_REGEX) && current_site.screenshot_exists?(file.filename, '105x63') %> + <% if file[:is_html] && current_site.screenshot_exists?(file[:path], '105x63') %>
    - +
    - <% elsif file.ext.match(Site::IMAGE_REGEX) && current_site.thumbnail_exists?(file.filename, '105x63') %> + <% elsif file[:is_image] && current_site.thumbnail_exists?(file[:path], '105x63') %>
    - +
    - <% else %>
    -
    <%= file.ext %>
    +
    <%= file[:ext] %>
    <% end %> - <% if file.filename.length > 14 %> - <%= file.filename.slice(0..14) %>… + <% if file[:name].length > 15 %> + <%= file[:name].slice(0..14) %>… <% else %> - <%= file.filename %> + <%= file[:name] %> <% end %>
    - <% if file.ext.match(/html|htm|txt|js|css|md/) %> - Edit + <% if file[:is_editable] %> + Edit <% end %> - <% if file.filename != 'index.html' %> - Delete + <% if file[:is_directory] %> + Manage <% end %> - + <% if !file[:is_root_index] %> + Delete + <% end %> +
    <% end %> @@ -158,7 +170,7 @@

    Confirm deletion

    - + + @@ -224,7 +237,6 @@ }) this.on("totaluploadprogress", function(progress, totalBytes, totalBytesSent) { - console.log('OH HI') showUploadProgress() $('#progressBar').css('display', 'block') $('#uploadingProgress').css('width', progress+'%') @@ -232,3 +244,24 @@ } } + + \ No newline at end of file diff --git a/views/site_files/text_editor.erb b/views/site_files/text_editor.erb index ef28662e..53b100a8 100644 --- a/views/site_files/text_editor.erb +++ b/views/site_files/text_editor.erb @@ -15,7 +15,7 @@
    -

    Editing <%= params[:filename] %>

    +

    Editing <%= @filename %>

    @@ -120,11 +120,14 @@ function saveTextFile(quit) { $.ajax({ - url: '/site_files/save/<%= params[:filename] %>?csrf_token=<%= csrf_token %>', + url: '/site_files/save/<%= @filename %>?csrf_token=<%= csrf_token %>', data: editor.getValue(), processData: false, contentType: false, type: 'POST', + error: function(jqXHR, textStatus, errorThrown) { + alert('There has been an error saving your file, please try again. If it continues to fail, make a copy of the file locally so you don\'t lose your changes!') + }, success: function(response){ if(response == 'ok') { if(quit === true) { diff --git a/workers/screenshot_worker.rb b/workers/screenshot_worker.rb index 5610eb5e..e18e28c4 100644 --- a/workers/screenshot_worker.rb +++ b/workers/screenshot_worker.rb @@ -27,24 +27,25 @@ module Phantomjs end class ScreenshotWorker - SCREENSHOTS_PATH = File.join DIR_ROOT, 'public', 'site_screenshots' + SCREENSHOTS_PATH = Site::SCREENSHOTS_ROOT include Sidekiq::Worker sidekiq_options queue: :screenshots, retry: 3, backtrace: true - def perform(username, filename) + def perform(username, path) + path = "/#{path}" unless path[0] == '/' screenshot = Tempfile.new 'neocities_screenshot' screenshot.close screenshot_output_path = screenshot.path+'.png' begin - f = Screencap::Fetcher.new("http://#{username}.neocities.org/#{filename}") + f = Screencap::Fetcher.new("http://#{username}.neocities.org#{path}") f.fetch( output: screenshot_output_path, width: 1280, height: 720 ) rescue Timeout::Error - puts "#{username}/#{filename} is timing out, discontinuing" + puts "#{username}/#{path} is timing out, discontinuing" site = Site[username: username] site.update is_crashing: true @@ -54,7 +55,7 @@ class ScreenshotWorker EmailWorker.perform_async({ from: 'web@neocities.org', to: site.email, - subject: "[NeoCities] The web page \"#{filename}\" on your site (#{username}.neocities.org) is slow", + subject: "[NeoCities] The web page \"#{path}\" on your site (#{username}.neocities.org) is slow", body: "Hi there! This is an automated email to inform you that we're having issues loading your site to take a "+ "screenshot. It is possible that this is an error specific to our screenshot program, but it is much more "+ "likely that your site is too slow to be used with browsers. We don't want Neocities sites crashing browsers, "+ @@ -79,10 +80,12 @@ class ScreenshotWorker img = img_list.reverse.flatten_images user_screenshots_path = File.join SCREENSHOTS_PATH, username - FileUtils.mkdir_p user_screenshots_path + screenshot_path = File.join user_screenshots_path, File.dirname(path) + + FileUtils.mkdir_p screenshot_path unless Dir.exists?(screenshot_path) Site::SCREENSHOT_RESOLUTIONS.each do |res| - img.scale(*res.split('x').collect {|r| r.to_i}).write(File.join(user_screenshots_path, "#{filename}.#{res}.jpg")) { + img.scale(*res.split('x').collect {|r| r.to_i}).write(File.join(user_screenshots_path, "#{path}.#{res}.jpg")) { self.quality = 90 } end diff --git a/workers/thumbnail_worker.rb b/workers/thumbnail_worker.rb index d09881c4..9f0a558f 100644 --- a/workers/thumbnail_worker.rb +++ b/workers/thumbnail_worker.rb @@ -1,25 +1,26 @@ require 'RMagick' class ThumbnailWorker - THUMBNAILS_PATH = File.join DIR_ROOT, 'public', 'site_thumbnails' + THUMBNAILS_PATH = Site::THUMBNAILS_ROOT include Sidekiq::Worker sidekiq_options queue: :thumbnails, retry: 3, backtrace: true - def perform(username, filename) + def perform(username, path) img_list = Magick::ImageList.new - img_list.from_blob File.read(File.join(Site::SITE_FILES_ROOT, username, filename)) + img_list.from_blob File.read(File.join(Site::SITE_FILES_ROOT, username, path)) img = img_list.first user_thumbnails_path = File.join THUMBNAILS_PATH, username FileUtils.mkdir_p user_thumbnails_path + FileUtils.mkdir_p File.join(user_thumbnails_path, File.dirname(path)) Site::THUMBNAIL_RESOLUTIONS.each do |res| resimg = img.resize_to_fit(*res.split('x').collect {|r| r.to_i}) - format = File.extname(filename).gsub('.', '') + format = File.extname(path).gsub('.', '') save_ext = format.match(Site::LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - resimg.write(File.join(user_thumbnails_path, "#{filename}.#{res}.#{save_ext}")) { + resimg.write(File.join(user_thumbnails_path, "#{path}.#{res}.#{save_ext}")) { self.quality = 90 } end