fix tag tests, start on education site creation, updated test code

This commit is contained in:
Kyle Drake 2015-05-06 14:22:55 -07:00
parent f00e5d0757
commit 49defcd0c7
14 changed files with 627 additions and 623 deletions

View file

@ -20,15 +20,15 @@ GEM
byebug (2.7.0)
columnize (~> 0.3)
debugger-linecache (~> 1.2)
capybara (2.4.1)
capybara (2.4.4)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
capybara_minitest_spec (1.0.1)
capybara_minitest_spec (1.0.5)
capybara (>= 2)
minitest (>= 2)
minitest (>= 4)
celluloid (0.15.2)
timers (~> 1.1.0)
climate_control (0.0.3)
@ -99,8 +99,8 @@ GEM
metaclass (0.0.4)
method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.6.0)
minitest (5.3.1)
mini_portile (0.6.2)
minitest (5.6.1)
minitest-reporters (1.0.2)
ansi
builder
@ -108,14 +108,14 @@ GEM
powerbar
mocha (1.0.0)
metaclass (~> 0.0.1)
multi_json (1.10.1)
multi_json (1.11.0)
multipart-post (2.0.0)
netrc (0.10.3)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
pg (0.17.1)
phantomjs (1.9.7.1)
poltergeist (1.5.1)
poltergeist (1.6.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
@ -133,14 +133,14 @@ GEM
pry (~> 0.9.12)
puma (2.8.1)
rack (>= 1.1, < 2.0)
rack (1.5.2)
rack (1.6.0)
rack-cache (1.2)
rack (>= 0.4)
rack-protection (1.5.2)
rack
rack-recaptcha (0.6.6)
json
rack-test (0.6.2)
rack-test (0.6.3)
rack (>= 1.0)
rack_session_access (0.1.1)
builder (>= 2.0.0)
@ -226,7 +226,9 @@ GEM
webmock (1.17.4)
addressable (>= 2.2.7)
crack (>= 0.3.2)
websocket-driver (0.3.4)
websocket-driver (0.5.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
xpath (2.0.0)
nokogiri (~> 1.3)
zipruby (0.3.6)

View file

@ -7,6 +7,7 @@ end
post '/api/upload' do
require_api_credentials
files = []
params.each do |k,v|
next unless v.is_a?(Hash) && v[:tempfile]

View file

@ -16,9 +16,11 @@ def new_recaptcha_valid?
end
end
CREATE_MATCH_REGEX = /^username$|^password$|^email$|^new_tags_string$|^is_education$/
post '/create_validate_all' do
content_type :json
fields = params.select {|p| p.match /^username$|^password$|^email$|^new_tags_string$/}
fields = params.select {|p| p.match CREATE_MATCH_REGEX}
site = Site.new fields
@ -33,11 +35,12 @@ end
post '/create_validate' do
content_type :json
if !params[:field].match /^username$|^password$|^email$|^new_tags_string$/
if !params[:field].match CREATE_MATCH_REGEX
return {error: 'not a valid field'}.to_json
end
site = Site.new(params[:field] => params[:value])
site.is_education = params[:is_education]
site.valid?
field_sym = params[:field].to_sym
@ -58,7 +61,8 @@ post '/create' do
username: params[:username],
password: params[:password],
email: params[:email],
new_tags_string: params[:tags],
new_tags_string: params[:new_tags_string],
is_education: params[:is_education] == 'true' ? true : false,
ip: request.ip
)

View file

@ -34,7 +34,7 @@ get '/?' do
@sites_count = SimpleCache.get :sites_count
end
erb :index, layout: false
erb :index, layout: :index_layout
end
get '/welcome' do
@ -44,7 +44,8 @@ get '/welcome' do
end
get '/education' do
erb :education, layout: false
redirect '/' if signed_in?
erb :education, layout: :index_layout
end
get '/tutorials' do

View file

@ -0,0 +1,9 @@
Sequel.migration do
up {
add_column :sites, :is_education, :boolean, default: false
}
down {
drop_column :sites, :is_education
}
end

View file

@ -835,6 +835,18 @@ class Site < Sequel::Model
new_tags.compact!
@new_filtered_tags = []
if values[:is_education] == true
if new?
if @new_tags_string.nil? || @new_tags_string.empty?
errors.add :new_tags_string, 'A Class Tag is required.'
end
if new_tags.length > 1
errors.add :new_tags_string, 'Must only have one tag'
end
end
end
if ((new? ? 0 : tags_dataset.count) + new_tags.length > 5)
errors.add :new_tags_string, 'Cannot have more than 5 tags for your site.'
end
@ -861,7 +873,7 @@ class Site < Sequel::Model
break
end
next if tags.collect {|t| t.name}.include? tag
next if !new? && tags.collect {|t| t.name}.include?(tag)
@new_filtered_tags << tag
@new_filtered_tags.uniq!

View file

@ -0,0 +1,61 @@
require_relative './environment.rb'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false)
end
describe 'signup' do
include Capybara::DSL
def fill_in_valid
@site = Fabricate.attributes_for(:site)
@class_tag = SecureRandom.uuid.gsub('-', '')[0..Tag::NAME_LENGTH_MAX-1]
fill_in 'username', with: @site[:username]
fill_in 'password', with: @site[:password]
fill_in 'email', with: @site[:email]
fill_in 'new_tags_string', with: @class_tag
end
before do
Capybara.default_driver = :poltergeist
Capybara.reset_sessions!
visit '/education'
page.must_have_content 'Neocities' # Used to force load wait
end
after do
Capybara.default_driver = :rack_test
end
it 'succeeds with valid data' do
fill_in_valid
click_button 'Create My Site'
page.must_have_content 'Welcome to Neocities'
index_file_path = File.join Site::SITE_FILES_ROOT, @site[:username], 'index.html'
File.exist?(index_file_path).must_equal true
site = Site[username: @site[:username]]
site.site_files.length.must_equal 4
site.site_changed.must_equal false
site.site_updated_at.must_equal nil
site.is_education.must_equal true
site.tags.length.must_equal 1
site.tags.first.name.must_equal @class_tag
end
it 'fails to create for existing site' do
@existing_site = Fabricate :site
fill_in_valid
fill_in :username, with: @existing_site.username
click_button 'Create My Site'
page.must_have_content 'already taken'
end
it 'fails for multiple tags' do
fill_in_valid
fill_in :new_tags_string, with: 'derp, ie'
click_button 'Create My Site'
page.must_have_content 'Must only have one tag'
end
end

View file

@ -5,3 +5,5 @@ Capybara.app = Sinatra::Application
def teardown
Capybara.reset_sessions!
end
Capybara.default_wait_time = 5

View file

@ -30,6 +30,7 @@ describe 'signup' do
Capybara.default_driver = :poltergeist
Capybara.reset_sessions!
visit_signup
page.must_have_content 'Neocities' # Used to force load wait
end
after do
@ -48,19 +49,15 @@ describe 'signup' do
site.site_files.length.must_equal 4
site.site_changed.must_equal false
site.site_updated_at.must_equal nil
site.is_education.must_equal false
site.ip.must_equal Site.hash_ip('127.0.0.1')
end
it 'fails to create for existing site' do
@existing_site = Fabricate :site
fill_in_valid
click_signup_button
page.must_have_content 'Welcome to Neocities'
Capybara.reset_sessions!
visit_signup
sleep 0.3
fill_in 'username', with: @site[:username]
fill_in 'password', with: @site[:password]
fill_in 'username', with: @existing_site.username
click_signup_button
page.must_have_content 'already taken'
end
@ -113,9 +110,6 @@ describe 'signup' do
page.must_have_content /email.+exists/
end
puts "$$$$$$$$$$$$$$$$$$$$$$ TODO FIX TAGS TESTS"
=begin
it 'succeeds with no tags' do
fill_in_valid
fill_in 'new_tags_string', with: ''
@ -179,9 +173,10 @@ puts "$$$$$$$$$$$$$$$$$$$$$$ TODO FIX TAGS TESTS"
fill_in 'new_tags_string', with: 'one, one'
click_signup_button
site = Site.last
page.must_have_content /Welcome to Neocities/
site = Site[username: @site[:username]]
site.tags.length.must_equal 1
site.tags.first.name.must_equal 'one'
end
=end
end

View file

@ -167,14 +167,6 @@ describe 'api upload' do
'/' => 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

View file

@ -0,0 +1,37 @@
<script src="/js/app.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script>
$('#createSiteForm').on('submit', function(obj) {
$.post('/create_validate_all', $(obj.target).serialize(), function(errors) {
if(errors.length == 0) {
$.post('/create', $('#createSiteForm').serialize(), function(res) {
window.location.href = '/welcome'
})
} else {
for(var i=0; i<errors.length;i++) {
if(errors[i][0] == 'captcha') {
var captchaDiv = $('#captcha-input')
captchaDiv.attr('data-original-title', errors[i][1])
captchaDiv.tooltip('show')
} else {
var ele = $('input[name='+errors[i][0]+']')
ele.attr('data-original-title', errors[i][1])
ele.tooltip('show')
}
}
}
})
})
$('input[type=text],input[type=password]').on('change focusout', function(obj) {
$.post('/create_validate', {field: obj.target.name, value: obj.target.value, is_education: $('input[name=is_education]')[0].value, csrf_token: '<%= csrf_token %>'}, function(res) {
if(res.result == 'ok') {
return $(obj.target).tooltip('hide')
}
$(obj.target).attr('data-original-title', res.error)
$(obj.target).tooltip('show')
})
})
</script>

View file

@ -1,46 +1,4 @@
<!doctype html>
<!--[if IE 8 ]><html lang="en" class="ieAll ie8"><![endif]-->
<!--[if IE 9 ]><html lang="en" class="ieAll ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html lang="en"><!--<![endif]-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Neocities for Education</title>
<meta itemprop="name" content="Neocities.org" />
<meta itemprop="description" content="Create your own free home page, and do whatever you want with it." />
<meta name="description" content="Neocities is the new Geocities. Create your own free home page, and do whatever you want with it." />
<meta name="keywords" content="free website, html, css, learn to code, free hosting, build a website, create a web page" />
<link rel="canonical" href="//neocities.org" />
<meta property="og:title" content="Neocities"/>
<meta property="og:site_name" content="Neocities | neocities.org"/>
<meta property="og:type" content="website"/>
<meta property="og:image" content=""/>
<meta property="og:url" content="//www.neocities.org"/>
<meta property="og:description" content="Neocities is the new Geocities. Create your own free home page, and do whatever you want with it."/>
<link rel="shortcut icon" type="image/ico" href="/favicon.ico?v=4" />
<link rel="apple-touch-icon-precomposed" href="#apple-icon-144.png" />
<link rel="apple-touch-startup-image" href="#startup.png" />
<!-- Mobile Meta -->
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1" />
<link href="/css/neo.css" rel="stylesheet" type="text/css" media="all"/>
<!--[if lt IE 9]>
<script type="text/javascript" src="/js/html5.min.js"></script>
<![endif]-->
<script src="/js/jquery-1.11.0.min.js"></script>
<script src='https://www.google.com/recaptcha/api.js'></script>
</head>
<body class="hp education">
<body class="hp education">
<a id="new"></a>
<div class="page">
@ -83,21 +41,21 @@
<% end %>
<div class="int-Logo hp-Logo">
<a href="/" title="back to home">
<a href="/" title="back to home">
<span class="hidden">Neocities.org</span>
<img src="/img/cat.png" alt="Neocities.org" />
</a>
</a>
</div>
<section class="header-Intro">
<h1 class="logo header-Content content">
<h1 class="logo header-Content content">
<span class="hidden">Neocities for Education</span>
<img src="/img/neocities-logo-education.png" alt="Neocities.org" />
</h1>
</h1>
</section>
<div class="header-Outro">
<div class="row header-Content content">
<div class="row header-Content content">
<div class="col intro">
<h3 class="delta">A great place to learn how to build websites</h2>
<img src="/img/heartcat.png" class="float-Right">
@ -126,6 +84,7 @@
<% else %>
<form id="createSiteForm" class="signup-Form" onsubmit="return false">
<input type="hidden" name="csrf_token" value="<%= csrf_token %>">
<input type="hidden" name="is_education" value="true">
<fieldset class="content">
<h2 class="gamma">Class Sign Up</h2>
<hr />
@ -183,7 +142,7 @@
</div> <!-- end .col-50 -->
</div> <!-- end .row -->
</div> <!-- end .row -->
</div> <!-- end .header-Outro -->
@ -192,8 +151,8 @@
<main class="content-Base">
<div class="section instructor-quotes">
<h2 class="delta">What Instructors Say</h2>
<div class="row content">
<h2 class="delta">What Instructors Say</h2>
<div class="row content">
<div class="col col-33">
<div class="image" style="width:130px;height:130px;background:#aaa"></div>
<h3>Instructor Name<br>
@ -221,21 +180,21 @@
<p>"Neocities was an excellent resource for my students - it made everything very easy.
The Neocities team did a great job responding to any questions I had."</p>
</div>
</div>
</div>
</div>
<div class="section support">
<h2>Support Us</h2>
<div class="row quote">
<h2>Support Us</h2>
<div class="row quote">
<div class="col" style="">
<p>Neocities is funded directly by our community through supporter plans and donations. We will never sell users' personal data or embed advertising on member sites. Your support allows us to pay for server costs and continue working on Neocities full-time. You can support us by making a <a href="/donate">one-time donation</a> or by <a href="/plan">subscribing for $5/month</a>.</p>
</div>
</div>
</div>
</div>
</main>
<footer class="footer-Base" role="contentinfo">
<div class="footer-Intro">
<div class="footer-Intro">
<div class="footer-Content">
<div class="row">
<div class="col col-33">
@ -271,47 +230,11 @@
</div>
</div>
</div>
</div>
</div>
<%== erb :'_footer', layout: false %>
<%== erb :'_footer', layout: false %>
</footer>
</div>
<script src="/js/bootstrap.min.js"></script>
<script>
$('#createSiteForm').on('submit', function(obj) {
$.post('/create_validate_all', $(obj.target).serialize(), function(errors) {
if(errors.length == 0) {
$.post('/create', $('#createSiteForm').serialize(), function(res) {
window.location.href = '/welcome'
})
} else {
for(var i=0; i<errors.length;i++) {
if(errors[i][0] == 'captcha') {
var captchaDiv = $('#captcha-input')
captchaDiv.attr('data-original-title', errors[i][1])
captchaDiv.tooltip('show')
} else {
var ele = $('input[name='+errors[i][0]+']')
ele.attr('data-original-title', errors[i][1])
ele.tooltip('show')
}
}
}
})
})
$('input[type=text],input[type=password]').on('change focusout', function(obj) {
$.post('/create_validate', {field: obj.target.name, value: obj.target.value, csrf_token: '<%= csrf_token %>'}, function(res) {
if(res.result == 'ok') {
return $(obj.target).tooltip('hide')
}
$(obj.target).attr('data-original-title', res.error)
$(obj.target).tooltip('show')
})
})
</script>
</body>
</html>
<%== erb :'_index_signup_script', layout: false %>
</body>

View file

@ -1,46 +1,4 @@
<!doctype html>
<!--[if IE 8 ]><html lang="en" class="ieAll ie8"><![endif]-->
<!--[if IE 9 ]><html lang="en" class="ieAll ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html lang="en"><!--<![endif]-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Neocities: Create your free website now!</title>
<meta itemprop="name" content="Neocities.org" />
<meta itemprop="description" content="Create your own free home page, and do whatever you want with it." />
<meta name="description" content="Neocities is the new Geocities. Create your own free home page, and do whatever you want with it." />
<meta name="keywords" content="free website, html, css, learn to code, free hosting, build a website, create a web page" />
<link rel="canonical" href="//neocities.org" />
<meta property="og:title" content="Neocities"/>
<meta property="og:site_name" content="Neocities | neocities.org"/>
<meta property="og:type" content="website"/>
<meta property="og:image" content=""/>
<meta property="og:url" content="//www.neocities.org"/>
<meta property="og:description" content="Neocities is the new Geocities. Create your own free home page, and do whatever you want with it."/>
<link rel="shortcut icon" type="image/ico" href="/favicon.ico?v=4" />
<link rel="apple-touch-icon-precomposed" href="#apple-icon-144.png" />
<link rel="apple-touch-startup-image" href="#startup.png" />
<!-- Mobile Meta -->
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1" />
<link href="/css/neo.css" rel="stylesheet" type="text/css" media="all"/>
<!--[if lt IE 9]>
<script type="text/javascript" src="/js/html5.min.js"></script>
<![endif]-->
<script src="/js/jquery-1.11.0.min.js"></script>
<script src='https://www.google.com/recaptcha/api.js'></script>
</head>
<body class="hp">
<body class="hp">
<a id="new"></a>
<div class="page">
@ -83,21 +41,21 @@
<% end %>
<div class="int-Logo hp-Logo">
<a href="/" title="back to home">
<a href="/" title="back to home">
<span class="hidden">Neocities.org</span>
<img src="/img/cat.png" alt="Neocities.org" />
</a>
</a>
</div>
<section class="header-Intro">
<h1 class="logo header-Content content">
<h1 class="logo header-Content content">
<span class="hidden">Neocities.org</span>
<img src="/img/neocities-Logo.png" alt="Neocities.org" />
</h1>
</h1>
</section>
<div class="header-Outro">
<div class="row header-Content content">
<div class="row header-Content content">
<div class="col intro">
<h2 class="section-header">Create your own free web site, and discover new ones.</h2>
<p class="intro-text">
@ -138,6 +96,7 @@
<% else %>
<form id="createSiteForm" class="signup-Form" onsubmit="return false">
<input type="hidden" name="csrf_token" value="<%= csrf_token %>">
<input type="hidden" name="is_education" value="false">
<fieldset class="content">
<h2 class="gamma">Sign up for free</h2>
<hr />
@ -195,7 +154,7 @@
</div> <!-- end .col-50 -->
</div> <!-- end .row -->
</div> <!-- end .row -->
</div> <!-- end .header-Outro -->
@ -204,11 +163,11 @@
<main class="content-Base">
<div class="section featured-Websites">
<h2 class="delta">Featured Sites</h2>
<!--
<div class="nav prev"></div>
-->
<ul class="website-Gallery hp-Gallery">
<h2 class="delta">Featured Sites</h2>
<!--
<div class="nav prev"></div>
-->
<ul class="website-Gallery hp-Gallery">
<% Site.featured.each do |site| %>
<li>
<a href="<%= site.uri %>" title="<%= site.title %>" target="_blank">
@ -216,52 +175,52 @@
</a>
</li>
<% end %>
</ul>
<!--
<div class="nav next"></div>
-->
<a href="/browse" class="btn-Action float-Right">Browse all sites</a>
</ul>
<!--
<div class="nav next"></div>
-->
<a href="/browse" class="btn-Action float-Right">Browse all sites</a>
</div>
<div class="section previews">
<h2 class="delta">Our mission: To make the web fun again by giving you back control of how you express yourself online.</h2>
<h2 class="delta">Our mission: To make the web fun again by giving you back control of how you express yourself online.</h2>
<div class="row content">
<div class="row content">
<div class="col col-50"><div class="screenshot" style="background-image:url(/img/front-editor-screenshot.png)"></div></div>
<div class="col col-50 text">
<h3><i class="fa fa-edit"></i> HTML editor, right in your browser</h3>
<p>No tools needed. With our easy-to-use HTML editor, you're ready to start building your awesome web site right now.</p>
<p>If you'd rather use your favorite desktop editor, no problem. Uploading files is as easy as drag-n-drop.</p>
</div>
</div>
</div>
<div class="row content right">
<div class="row content right">
<div class="col col-50"><div class="screenshot" style="background-image:url(/img/front-browse-screenshot.png);"></div></div>
<div class="col col-50 text">
<h3><i class="fa fa-globe"></i> It's time to bring back web surfing.</h3>
<p>All Neocities sites are viewable in our <a href="/browse">website gallery</a>. And it's easy to browse sites with our optional surf bar.</p>
<p>Using tags (our version of Web Rings) you can easily discover new sites related to your interests.</p>
</div>
</div>
</div>
<div class="row content">
<div class="row content">
<div class="col col-50"><div class="screenshot" style="background-image:url(/img/front-follow-screenshot.png);"></div></div>
<div class="col col-50 text">
<h3><i class="fa fa-user-plus"></i> Follow your favorite Neocities sites</h3>
<p>Keep track of all your favorite sites by following them. Any changes to the sites automatically show up in your news feed. You'll also see what sites they follow.</p>
</div>
</div>
</div>
<div class="row content right">
<div class="row content right">
<div class="col col-50"><div class="screenshot" style="background-image:url(/img/front-comment-screenshot.png);"></div></div>
<div class="col col-50 text">
<h3><i class="fa fa-comments-o"></i> Web creativity plus community</h3>
<p>Interact with your favorite web builders by posting comments, and sharing their sites on your social network of choice.</p>
</div>
</div>
</div>
</div>
<section class="section features">
<section class="section features">
<div class="row">
<div class="col col-50">
<h3>
@ -309,13 +268,13 @@
</h3>
</div>
</div>
</section>
</section>
<% # erb :'plan/_pricing' %>
<% # erb :'plan/_pricing' %>
<section class="section bottom-signup">
<section class="section bottom-signup">
<h2>What are you waiting for? <a href="#new">Start building your web site now!</a></h2>
</section>
</section>
</main>
<footer class="footer-Base" role="contentinfo">
@ -356,47 +315,8 @@
</div>
</div>
</div>
<%== erb :'_footer', layout: false %>
</footer>
</div>
<script src="/js/app.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script>
$('#createSiteForm').on('submit', function(obj) {
$.post('/create_validate_all', $(obj.target).serialize(), function(errors) {
if(errors.length == 0) {
$.post('/create', $('#createSiteForm').serialize(), function(res) {
window.location.href = '/welcome'
})
} else {
for(var i=0; i<errors.length;i++) {
if(errors[i][0] == 'captcha') {
var captchaDiv = $('#captcha-input')
captchaDiv.attr('data-original-title', errors[i][1])
captchaDiv.tooltip('show')
} else {
var ele = $('input[name='+errors[i][0]+']')
ele.attr('data-original-title', errors[i][1])
ele.tooltip('show')
}
}
}
})
})
$('input[type=text],input[type=password]').on('change focusout', function(obj) {
$.post('/create_validate', {field: obj.target.name, value: obj.target.value, csrf_token: '<%= csrf_token %>'}, function(res) {
if(res.result == 'ok') {
return $(obj.target).tooltip('hide')
}
$(obj.target).attr('data-original-title', res.error)
$(obj.target).tooltip('show')
})
})
</script>
</body>
</html>
<%== erb :'_index_signup_script', layout: false %>
</body>

45
views/index_layout.erb Normal file
View file

@ -0,0 +1,45 @@
<!doctype html>
<!--[if IE 8 ]><html lang="en" class="ieAll ie8"><![endif]-->
<!--[if IE 9 ]><html lang="en" class="ieAll ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html lang="en"><!--<![endif]-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Neocities: Create your free website now!</title>
<meta itemprop="name" content="Neocities.org" />
<meta itemprop="description" content="Create your own free home page, and do whatever you want with it." />
<meta name="description" content="Neocities is the new Geocities. Create your own free home page, and do whatever you want with it." />
<meta name="keywords" content="free website, html, css, learn to code, free hosting, build a website, create a web page" />
<link rel="canonical" href="//neocities.org" />
<meta property="og:title" content="Neocities"/>
<meta property="og:site_name" content="Neocities | neocities.org"/>
<meta property="og:type" content="website"/>
<meta property="og:image" content=""/>
<meta property="og:url" content="//www.neocities.org"/>
<meta property="og:description" content="Neocities is the new Geocities. Create your own free home page, and do whatever you want with it."/>
<link rel="shortcut icon" type="image/ico" href="/favicon.ico?v=4" />
<link rel="apple-touch-icon-precomposed" href="#apple-icon-144.png" />
<link rel="apple-touch-startup-image" href="#startup.png" />
<!-- Mobile Meta -->
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1" />
<link href="/css/neo.css" rel="stylesheet" type="text/css" media="all"/>
<!--[if lt IE 9]>
<script type="text/javascript" src="/js/html5.min.js"></script>
<![endif]-->
<script src="/js/jquery-1.11.0.min.js"></script>
<script src='https://www.google.com/recaptcha/api.js'></script>
</head>
<%== yield %>
</html>