Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finished at last

  • Loading branch information...
commit b57cfbf020b42b3a24dea4b68a12501299bcdd52 1 parent 9cc14cf
@ddemaree authored
View
5 Gemfile
@@ -8,6 +8,8 @@ gem 'sprockets', '~> 2.0'
gem 'sass', '~> 3.1'
gem 'coffee-script'
+# gem 'primer', :path => '../primer'
+
group :development do
gem 'heroku'
gem 'shotgun'
@@ -17,6 +19,9 @@ group :development, :test do
gem 'sqlite3'
gem 'rspec'
gem 'rack-test', "~> 0.5.7"
+ gem 'capybara'
+ gem 'database_cleaner'
+ gem 'factory_girl'
end
group :production do
View
25 Gemfile.lock
@@ -15,15 +15,28 @@ GEM
addressable (2.2.6)
arel (2.2.1)
builder (3.0.0)
+ capybara (1.0.1)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ selenium-webdriver (~> 2.0)
+ xpath (~> 0.1.4)
+ childprocess (0.2.2)
+ ffi (~> 1.0.6)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.1.2)
daemons (1.1.4)
+ database_cleaner (0.6.7)
diff-lcs (1.1.3)
eventmachine (0.12.10)
execjs (1.2.9)
multi_json (~> 1.0)
+ factory_girl (2.2.0)
+ activesupport
+ ffi (1.0.9)
heroku (2.11.0)
launchy (>= 0.3.2)
rest-client (~> 1.6.1)
@@ -31,10 +44,12 @@ GEM
term-ansicolor (~> 1.0.5)
hike (1.2.1)
i18n (0.6.0)
+ json_pure (1.6.1)
launchy (2.0.5)
addressable (~> 2.2.6)
mime-types (1.17.2)
multi_json (1.0.3)
+ nokogiri (1.5.0)
pg (0.11.0)
rack (1.3.5)
rack-protection (1.1.4)
@@ -53,6 +68,11 @@ GEM
rspec-mocks (2.7.0)
rubyzip (0.9.4)
sass (3.1.10)
+ selenium-webdriver (2.5.0)
+ childprocess (>= 0.2.1)
+ ffi (>= 1.0.7)
+ json_pure
+ rubyzip
shotgun (0.9)
rack (>= 1.0)
sinatra (1.3.1)
@@ -71,6 +91,8 @@ GEM
rack (>= 1.0.0)
tilt (1.3.3)
tzinfo (0.3.30)
+ xpath (0.1.4)
+ nokogiri (~> 1.3)
PLATFORMS
ruby
@@ -78,7 +100,10 @@ PLATFORMS
DEPENDENCIES
activerecord (~> 3.1)
activesupport (~> 3.1)
+ capybara
coffee-script
+ database_cleaner
+ factory_girl
heroku
pg
rack-test (~> 0.5.7)
View
30 Rakefile
@@ -2,11 +2,37 @@ task :environment do
require File.expand_path( "../config/environment", __FILE__ )
end
-# require 'rspec/core/rake_task'
-# RSpec::Core::RakeTask.new
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new
task :default => [:spec]
+namespace :assets do
+ def precompiled_assets
+ [].tap do |arr|
+ arr.concat Dir[File.join(APP_ROOT, "assets", "images", "*")].map { |f| File.basename(f) }
+ arr << "thirty_one.css"
+ arr << "application.js"
+ end
+ end
+
+ task :precompile => :environment do
+ target = File.join(APP_ROOT, "public", "assets")
+
+ @_env = ThirtyOne.sprockets_environment
+ @_env.each_logical_path do |logical_path|
+ next unless precompiled_assets.include?(logical_path)
+ if asset = @_env.find_asset(logical_path)
+ puts "Precompile #{logical_path}"
+ filename = File.join(target, asset.digest_path)
+ FileUtils.mkdir_p File.dirname(filename)
+ asset.write_to(filename)
+ # asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/
+ end
+ end
+ end
+end
+
namespace :db do
desc "Migrate the database"
task :migrate => [:environment] do
View
BIN  assets/images/disc-200px.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
34 assets/javascripts/application.js.coffee
@@ -3,36 +3,4 @@
#= require jquery-tmpl
#= require knockout
#= require_self
-#= require_tree
-
-$ ->
- # Get all the bits
- bitsJson = $('#bits_json').html()
- window.Bits = JSON.parse(bitsJson)
-
- # Get party bits data and make a party
- partyJson = $('#party_json').html()
- viewModel = new Party(JSON.parse(partyJson))
-
- $('label').each (i, label)->
- label = $(this)
- checkbox = $(this).find('input[type=checkbox]')
- checkbox.click (e)->
- console.log('Checkbox clicked')
-
- checkboxValue = checkbox.attr('value')
-
- link = $('<a href="#" class="label-trigger" />')
- link.data('bit', checkboxValue)
- link.attr('data-bind', "css: {'ko-enabled':true, 'checked':bitSelected('#{checkboxValue}')}")
- link.click (e)->
- console.log('Link clicked')
- e.preventDefault()
- e.stopPropagation()
- # checkbox.click()
- viewModel.toggleBit(checkboxValue)
-
- # label.wrap(link)
-
- # Bind the UI
- ko.applyBindings(viewModel)
View
74 assets/javascripts/components/party_view.js.coffee
@@ -0,0 +1,74 @@
+window.PartyView =
+ init: ()->
+ # Get all the bits
+ bitsJson = $('#bits_json').html()
+ window.Bits = JSON.parse(bitsJson)
+
+ # Get party bits data and make a party
+ partyJson = $('#party_json').html()
+ viewModel = new Party(JSON.parse(partyJson))
+
+ $('label').each (i, label)->
+ label = $(this)
+ checkbox = $(this).find('input[type=checkbox]')
+ checkbox.click (e)->
+ console.log('Checkbox clicked')
+
+ checkboxValue = checkbox.attr('value')
+
+ link = $('<a href="#" class="label-trigger" />')
+ link.data('bit', checkboxValue)
+ link.attr('data-bind', "css: {'ko-enabled':true, 'checked':bitSelected('#{checkboxValue}')}")
+ link.click (e)->
+ console.log('Link clicked')
+ e.preventDefault()
+ e.stopPropagation()
+ # checkbox.click()
+ viewModel.toggleBit(checkboxValue)
+
+ # label.wrap(link)
+
+ # Bind the UI
+ ko.applyBindings(viewModel)
+
+class window.Party
+ constructor: (bits) ->
+ @bits = ko.observableArray(bits)
+
+ toggleAll: (namespace) ->
+ nsBits = window.Bits[namespace]
+ if @bitsInNamespace(namespace).length == nsBits.length
+ for bitPath in nsBits
+ do (bitPath) =>
+ index = @bits().indexOf(bitPath)
+ @bits.splice(index, 1)
+ else
+ for bitPath in nsBits
+ do (bitPath) =>
+ unless @bits().indexOf(bitPath) > -1
+ @bits.push(bitPath);
+
+ toggleLabel: (ns) ->
+ nsBits = window.Bits[ns]
+ if @bitsInNamespace(ns).length == nsBits.length
+ "Uncheck all"
+ else
+ "Select all"
+
+ bitSelected: (bit)->
+ @bits().indexOf(bit) > -1
+
+ toggleBit: (bit)->
+ console.log('Bit toggled')
+ if (idx = @bits().indexOf(bit)) > -1
+ @bits.splice(idx, 1)
+ else
+ @bits.push(bit)
+
+ bitsInNamespace: (ns) ->
+ @bits().filter (bit)->
+ return bit.match('^'+ns)
+
+ hasNamespace: (ns) ->
+ @bits().some (bit) ->
+ return bit.match('^'+ns)
View
41 assets/javascripts/view_model.js.coffee
@@ -1,41 +0,0 @@
-class window.Party
- constructor: (bits) ->
- @bits = ko.observableArray(bits)
-
- toggleAll: (namespace) ->
- nsBits = window.Bits[namespace]
- if @bitsInNamespace(namespace).length == nsBits.length
- for bitPath in nsBits
- do (bitPath) =>
- index = @bits().indexOf(bitPath)
- @bits.splice(index, 1)
- else
- for bitPath in nsBits
- do (bitPath) =>
- unless @bits().indexOf(bitPath) > -1
- @bits.push(bitPath);
-
- toggleLabel: (ns) ->
- nsBits = window.Bits[ns]
- if @bitsInNamespace(ns).length == nsBits.length
- "Uncheck all"
- else
- "Select all"
-
- bitSelected: (bit)->
- @bits().indexOf(bit) > -1
-
- toggleBit: (bit)->
- console.log('Bit toggled')
- if (idx = @bits().indexOf(bit)) > -1
- @bits.splice(idx, 1)
- else
- @bits.push(bit)
-
- bitsInNamespace: (ns) ->
- @bits().filter (bit)->
- return bit.match('^'+ns)
-
- hasNamespace: (ns) ->
- @bits().some (bit) ->
- return bit.match('^'+ns)
View
5 assets/stylesheets/components/thanks.css.scss
@@ -0,0 +1,5 @@
+.thanks {
+ .party-info {
+
+ }
+}
View
4 assets/stylesheets/primer.css.scss
@@ -94,10 +94,10 @@ em {
}
:-moz-placeholder {
- color: lighten(#000, 75%);
+ color: lighten(#000, 85%);
}
::-webkit-input-placeholder {
- color: lighten(#000, 75%);
+ color: lighten(#000, 85%);
}
View
107 assets/stylesheets/thirty_one.css.scss
@@ -58,6 +58,18 @@ body {
font-family: $sans-serif-fonts;
}
+a {
+ color: #71868C;
+}
+
+a:visited {
+ color: darken(#71868C, 10%);
+}
+
+a:active {
+ color: darken(#71868C, 20%);
+}
+
.container {
margin: 0 auto;
width: percentage(300/320);
@@ -101,7 +113,13 @@ label, input, select, textarea {
.big-logo {
margin-bottom: 10px;
}
+ img {
+ width: 150px;
+ }
@media screen and (min-width:640px){
+ img {
+ width: 200px;
+ }
h1 {
font-size: 36px;
line-height: 40px;
@@ -257,13 +275,14 @@ label, input, select, textarea {
}
&.checked {
.label-text {
- background: mix(#fff, #ffa, 60%);
- color: #222;
+ background: mix(#fff, #ffa, 70%);
+ color: #313131;
text-shadow: 1px 1px 0 #fff;
}
}
&:active {
background-color: #e5e5e5;
+ background-color: mix(#fff, #71868C, 85%);
.label-text { background-color: transparent; }
}
}
@@ -289,7 +308,7 @@ label, input, select, textarea {
}
.day-header {
@extend .answer-area-header;
-
+
@media screen and (min-width: 1024px) {
.day-of-week, time {
display: block;
@@ -350,10 +369,9 @@ label, input, select, textarea {
.places-list-header {
@extend .answer-area-header;
padding: 10px;
-
+
h3 {
font-size: 17px;
- //padding-left: 5px;
}
}
.places-list-section {
@@ -431,7 +449,7 @@ label, input, select, textarea {
line-height: 16px;
padding: 10px 20px 12px;
text-shadow: 0 1px 1px rgba(#fff, 0.75);
-
+
&:hover {
background-position: 0 -15px;
color: #333;
@@ -449,7 +467,7 @@ label, input, select, textarea {
margin-top: 20px;
padding: 20px 0 80px;
text-align: center;
-
+
h3 {
font-size: 20px;
font-weight: 600;
@@ -457,15 +475,15 @@ label, input, select, textarea {
margin: 0 auto;
width: percentage(240/300);
}
-
+
ul, p, li {
- font-family: $serif-fonts;
+ font-family: $serif-fonts;
font-size: 15px;
line-height: 20px;
margin: 10px auto;
width: percentage(260/300);
}
-
+
ul {
list-style-type: square;
text-align: left;
@@ -477,20 +495,20 @@ label, input, select, textarea {
li + li {
margin-top: 5px;
}
-
+
@media screen and (min-width:768px) {
width: percentage(grid-span(9) / 700px);
}
}
-
+
p {
margin: 20px auto;
}
-
+
.btn {
margin-bottom: 10px;
width: percentage(280/300);
-
+
@media screen and (min-width: 320px){
width: percentage(440/460);
}
@@ -500,3 +518,64 @@ label, input, select, textarea {
}
}
}
+
+
+.thanks {
+ padding-bottom: 40px;
+
+ header {
+ padding-bottom: 20px;
+ }
+
+ .party-info {
+ @extend .answer-area;
+ font-family: $serif-fonts;
+ font-size: 15px;
+ line-height: 20px;
+
+ @media screen and (min-width:640px){
+ font-size: 17px;
+ line-height: 22px;
+ margin: 0 auto;
+ width: 620px;
+ }
+
+ .link, .email {
+ padding: 10px;
+ margin: 10px;
+ }
+
+ .link {
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 0;
+ padding-bottom: 20px;
+ }
+
+ h3 {
+ font-family: $sans-serif-fonts;
+ font-weight: 600;
+ font-size: 19px;
+ line-height: 22px;
+ }
+
+ p + p {
+ margin-top: 10px;
+ }
+
+ .secret-url {
+ font-family: $sans-serif-fonts;
+ font-size: 20px;
+ line-height: 22px;
+ margin: 15px 0;
+
+ @media screen and (min-width:640px){
+ font-size: 24px;
+ line-height: 26px;
+ }
+
+ a {
+ display: block;
+ }
+ }
+ }
+}
View
16 bin/nokogiri
@@ -0,0 +1,16 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Bundler.
+#
+# The application 'nokogiri' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'pathname'
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require 'rubygems'
+require 'bundler/setup'
+
+load Gem.bin_path('nokogiri', 'nokogiri')
View
4 config.ru
@@ -7,6 +7,10 @@ map '/assets' do
run ThirtyOne.sprockets_environment
end
+map '/test' do
+ run lambda { |e| [200, {"Content-Type" => "text/plain"}, ["Hello?"]] }
+end
+
map '/' do
run ThirtyOne::App
end
View
0  bits.yml → db/bits.yml
File renamed without changes
View
8 db/migrate/002_add_fields_to_party.rb
@@ -0,0 +1,8 @@
+class AddFieldsToParty < ActiveRecord::Migration
+ def change
+ change_table :parties do |t|
+ t.string :name
+ t.string :invitation_code
+ end
+ end
+end
View
7 db/migrate/003_add_unique_phrase_to_party.rb
@@ -0,0 +1,7 @@
+class AddUniquePhraseToParty < ActiveRecord::Migration
+ def change
+ change_table :parties do |t|
+ t.string :unique_phrase, :unique => true
+ end
+ end
+end
View
211 public/assets/application-2edc5ea869e0aac4b561d95445a98e2c.js
211 additions, 0 deletions not shown
View
BIN  public/assets/disc-200px-55a44a1d0fb5670a9632847e159b08eb.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
594 public/assets/thirty_one-dd122859a3f58da94999d877b32b044d.css
@@ -0,0 +1,594 @@
+/* Reset.less
+ * Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc).
+ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
+html, body {
+ margin: 0;
+ padding: 0; }
+
+h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, cite, code, del, dfn, em, img, q, s, samp, small, strike, strong, sub, sup, tt, var, dd, dl, dt, li, ol, ul, fieldset, form, label, legend, button, table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-weight: normal;
+ font-style: normal;
+ font-size: 100%;
+ line-height: 1;
+ font-family: inherit; }
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0; }
+
+ol, ul {
+ list-style: none; }
+
+q:before, q:after, blockquote:before, blockquote:after {
+ content: ""; }
+
+html {
+ overflow-y: scroll;
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%; }
+
+a:focus {
+ outline: thin dotted; }
+
+a:hover,
+a:active {
+ outline: 0; }
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block; }
+
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1; }
+
+audio:not([controls]) {
+ display: none; }
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline; }
+
+sup {
+ top: -0.5em; }
+
+sub {
+ bottom: -0.25em; }
+
+img {
+ border: 0;
+ -ms-interpolation-mode: bicubic; }
+
+button,
+input,
+select,
+textarea {
+ font-size: 100%;
+ margin: 0;
+ vertical-align: baseline;
+ *vertical-align: middle; }
+
+button,
+input {
+ line-height: normal;
+ *overflow: visible; }
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0; }
+
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer;
+ -webkit-appearance: button; }
+
+input[type="search"] {
+ -webkit-appearance: textfield;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box; }
+
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none; }
+
+textarea {
+ overflow: auto;
+ vertical-align: top; }
+html, body {
+ background-color: #fff; }
+
+body {
+ margin: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 18px;
+ color: #404040; }
+
+input,
+textarea,
+select,
+.uneditable-input {
+ border-radius: 3px;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1), 0 1px 1px white;
+ display: inline-block;
+ width: 210px;
+ height: 18px;
+ padding: 4px;
+ font-size: 13px;
+ line-height: 18px;
+ color: gray;
+ border: 1px solid #ccc; }
+
+input[type=file]:focus, input[type=checkbox]:focus, select:focus {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ outline: 1px dotted #666; }
+
+input[type=checkbox], input[type=radio] {
+ cursor: pointer; }
+
+input[type=checkbox], input[type=radio] {
+ width: auto;
+ height: auto;
+ padding: 0;
+ margin: 3px 0;
+ *margin-top: 0;
+ /* IE6-7 */
+ line-height: normal;
+ border: none; }
+
+strong {
+ font-style: inherit;
+ font-weight: bold; }
+
+em {
+ font-style: italic;
+ font-weight: inherit;
+ line-height: inherit; }
+
+:-moz-placeholder {
+ color: #d9d9d9; }
+
+::-webkit-input-placeholder {
+ color: #d9d9d9; }
+.wf-loading * {
+ visibility: hidden; }
+
+html, body {
+ background: #f2f1f1; }
+
+body {
+ min-width: 320px;
+ font-family: "ff-dagny-web-pro", Helvetica, Arial, sans-serif; }
+
+a {
+ color: #71868C; }
+
+a:visited {
+ color: #5a6b70; }
+
+a:active {
+ color: #435054; }
+
+.container {
+ margin: 0 auto;
+ width: 93.75%; }
+ @media screen and (min-width: 320px) {
+ .container {
+ width: 95.833%; } }
+ @media screen and (min-width: 768px) {
+ .container {
+ width: 91.146%; } }
+ @media screen and (min-width: 1024px) {
+ .container {
+ width: 940px; } }
+
+.group {
+ /* For modern browsers */
+ /* For IE 6/7 (trigger hasLayout) */ }
+ .group:before, .group:after {
+ content: "";
+ display: table; }
+ .group:after {
+ clear: both; }
+ .group {
+ zoom: 1; }
+
+label, input, select, textarea {
+ font-family: "ff-dagny-web-pro", Helvetica, Arial, sans-serif; }
+
+.banner {
+ padding: 40px 0;
+ text-align: center; }
+ .banner h1 {
+ margin: 0 auto;
+ font-size: 27px;
+ line-height: 30px; }
+ .banner p {
+ color: #666;
+ font-weight: 300;
+ font-family: "ff-tisa-web-pro", Georgia, serif;
+ font-size: 15px;
+ line-height: 20px;
+ margin: 10px auto 0;
+ width: 90%; }
+ .banner .big-logo {
+ margin-bottom: 10px; }
+ .banner img {
+ width: 150px; }
+ @media screen and (min-width:640px) {
+ .banner img {
+ width: 200px; }
+ .banner h1 {
+ font-size: 36px;
+ line-height: 40px; }
+ .banner p {
+ font-size: 20px;
+ line-height: 23px; } }
+ @media screen and (min-width:940px) {
+ .banner h1 {
+ font-size: 40px;
+ line-height: 45px;
+ width: 61.702%; }
+ .banner p {
+ width: 68.085%; } }
+
+.help-block {
+ color: #a6a6a6; }
+
+.field + .field {
+ margin-top: 15px; }
+
+.section {
+ /* For modern browsers */
+ /* For IE 6/7 (trigger hasLayout) */
+ border-top: 1px solid #ccc;
+ padding-top: 20px;
+ padding-bottom: 20px; }
+ .section:before, .section:after {
+ content: "";
+ display: table; }
+ .section:after {
+ clear: both; }
+ .section {
+ zoom: 1; }
+ .section + .section {
+ margin-top: 25px; }
+
+.section-info,
+.section-body {
+ width: 93.333%;
+ margin: 0 auto; }
+ @media screen and (min-width: 320px) {
+ .section-info,
+ .section-body {
+ width: 95.652%; } }
+ @media screen and (min-width: 768px) {
+ .section-info,
+ .section-body {
+ width: 97.326%; } }
+ @media screen and (min-width:768px) {
+ .section-info,
+ .section-body {
+ margin: 0; } }
+
+.section-info {
+ padding-bottom: 15px; }
+ .section-info h2 {
+ font-family: "ff-dagny-web-pro", Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ font-size: 24px;
+ line-height: 26px;
+ margin-bottom: 8px; }
+ .section-info p {
+ font-family: "ff-tisa-web-pro", Georgia, serif;
+ font-size: 15px;
+ line-height: 20px;
+ font-weight: 400;
+ color: #666; }
+ .section-info p + p {
+ margin-top: 8px; }
+ @media screen and (min-width:768px) {
+ .section-info {
+ float: left;
+ width: 31.429%; } }
+ @media screen and (min-width: 940px) {
+ .section-info {
+ width: 29.787%; } }
+
+@media screen and (min-width:768px) {
+ .section-body {
+ float: right;
+ width: 65.714%; } }
+@media screen and (min-width: 1024px) {
+ .section-body {
+ width: 68.085%; } }
+
+.answer-area, .places-section .places-list, .thanks .party-info {
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ background: #fff;
+ box-shadow: 0 3px 3px rgba(204, 202, 202, 0.3);
+ border-color: #ccc; }
+
+.answer-area-header, .days-section .day-header, .places-section .places-list-header {
+ font-size: 15px;
+ line-height: 16px;
+ padding: 5px;
+ border-bottom: 1px solid #bbb; }
+ .answer-area-header h3, .days-section .day-header h3, .places-section .places-list-header h3 {
+ display: inline; }
+ .answer-area-header a, .days-section .day-header a, .places-section .places-list-header a {
+ font-size: 13px;
+ padding: 10px 5px;
+ text-decoration: none; }
+
+.label-trigger {
+ color: #444;
+ display: block;
+ text-decoration: none; }
+ .label-trigger:active {
+ background-color: #e5e5e5; }
+ .label-trigger:active .label-text {
+ background-color: transparent; }
+
+.obvious-label, .days-section label, .places-section label {
+ border-bottom: 1px solid #eee;
+ display: block;
+ font-family: "ff-dagny-web-pro", Helvetica, Arial, sans-serif;
+ font-size: 17px;
+ line-height: 20px;
+ padding: 10px;
+ text-align: left;
+ width: auto; }
+ .obvious-label input[type=checkbox], .days-section label input[type=checkbox], .places-section label input[type=checkbox] {
+ font-size: 17px; }
+.obvious-label.checked .label-text, .days-section label.checked .label-text, .places-section label.checked .label-text {
+ background: #ffffe5;
+ color: #313131;
+ text-shadow: 1px 1px 0 #fff; }
+.obvious-label:active, .days-section label:active, .places-section label:active {
+ background-color: #e5e5e5;
+ background-color: #e9eced; }
+ .obvious-label:active .label-text, .days-section label:active .label-text, .places-section label:active .label-text {
+ background-color: transparent; }
+
+.email-section .help-block {
+ margin-top: 5px; }
+.email-section input[type=text] {
+ font-size: 24px;
+ height: 28px;
+ width: 96.429%; }
+
+.days-section .answer-area, .days-section .places-section .places-list, .places-section .days-section .places-list, .days-section .thanks .party-info, .thanks .days-section .party-info {
+ /* For modern browsers */
+ /* For IE 6/7 (trigger hasLayout) */
+ margin: 0 -5px;
+ padding: 5px; }
+ .days-section .answer-area:before, .days-section .places-section .places-list:before, .places-section .days-section .places-list:before, .days-section .thanks .party-info:before, .thanks .days-section .party-info:before, .days-section .answer-area:after, .days-section .places-section .places-list:after, .places-section .days-section .places-list:after, .days-section .thanks .party-info:after, .thanks .days-section .party-info:after {
+ content: "";
+ display: table; }
+ .days-section .answer-area:after, .days-section .places-section .places-list:after, .places-section .days-section .places-list:after, .days-section .thanks .party-info:after, .thanks .days-section .party-info:after {
+ clear: both; }
+ .days-section .answer-area, .days-section .places-section .places-list, .places-section .days-section .places-list, .days-section .thanks .party-info, .thanks .days-section .party-info {
+ zoom: 1; }
+@media screen and (min-width: 1024px) {
+ .days-section .day-header .day-of-week, .days-section .day-header time {
+ display: block;
+ padding: 0 5px;
+ line-height: 22px; }
+ .days-section .day-header time {
+ font-size: 22px; }
+ .days-section .day-header a {
+ display: block;
+ padding: 10px 5px; } }
+.days-section .day {
+ margin-bottom: 15px; }
+ @media screen and (min-width: 1024px) {
+ .days-section .day {
+ border-left: 1px solid #bbb;
+ float: left;
+ margin-bottom: 0;
+ position: relative;
+ width: 126px; }
+ .days-section .day:nth-child(1) {
+ width: 126px;
+ border: 0; } }
+@media screen and (min-width: 1024px) {
+ .days-section label {
+ font-size: 14px; } }
+@media screen and (min-width: 1024px) {
+ .days-section li:last-child label {
+ border-bottom: 0; } }
+.days-section input[type=checkbox] {
+ width: 16px; }
+
+.places-section .places-list {
+ margin: 0 -5px;
+ padding: 5px; }
+.places-section .checkbox-sections {
+ /* For modern browsers */
+ /* For IE 6/7 (trigger hasLayout) */ }
+ .places-section .checkbox-sections:before, .places-section .checkbox-sections:after {
+ content: "";
+ display: table; }
+ .places-section .checkbox-sections:after {
+ clear: both; }
+ .places-section .checkbox-sections {
+ zoom: 1; }
+.places-section .places-list-header {
+ padding: 10px; }
+ .places-section .places-list-header h3 {
+ font-size: 17px; }
+@media screen and (min-width:1024px) {
+ .places-section .places-list-section {
+ float: left;
+ width: 210px; }
+ .places-section .places-list-section.notes-section {
+ clear: both;
+ float: none;
+ width: auto; }
+ .places-section .places-list-section#drinks-section {
+ width: 212px; } }
+.places-section .places-list-section + .places-list-section {
+ margin-top: 15px; }
+ @media screen and (min-width:1024px) {
+ .places-section .places-list-section + .places-list-section {
+ margin-top: 0; }
+ .places-section .places-list-section + .places-list-section:first-child {
+ margin-left: 0; } }
+.places-section .places-list-section.notes-section {
+ margin-top: 15px; }
+.places-section .notes {
+ padding: 15px 10px 10px; }
+ .places-section .notes textarea {
+ min-height: 60px;
+ padding: 5px;
+ width: 96.109%; }
+ @media screen and (min-width:1024px) {
+ .places-section .notes textarea {
+ width: 98.107%; } }
+.places-section ul {
+ /* For modern browsers */
+ /* For IE 6/7 (trigger hasLayout) */ }
+ .places-section ul:before, .places-section ul:after {
+ content: "";
+ display: table; }
+ .places-section ul:after {
+ clear: both; }
+ .places-section ul {
+ zoom: 1; }
+@media screen and (min-width:1024px) {
+ .places-section label {
+ font-size: 15px; } }
+
+.btn {
+ background-color: #e5e5e5;
+ background-repeat: no-repeat;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(white), color-stop(25%, white), to(#e5e5e5));
+ background-image: -webkit-linear-gradient(white, white 25%, #e5e5e5);
+ background-image: -moz-linear-gradient(white, white 25%, #e5e5e5);
+ background-image: -ms-linear-gradient(white, white 25%, #e5e5e5);
+ background-image: -o-linear-gradient(white, white 25%, #e5e5e5);
+ background-image: linear-gradient(white, white 25%, #e5e5e5);
+ cursor: pointer;
+ display: inline-block;
+ border: 1px solid #ccc;
+ border-bottom-color: #bbb;
+ border-radius: 4px;
+ color: #333;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 16px;
+ padding: 10px 20px 12px;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); }
+ .btn:hover {
+ background-position: 0 -15px;
+ color: #333;
+ text-decoration: none; }
+ .btn:focus {
+ outline: 1px dotted #666; }
+
+.actions {
+ border-top: 1px solid #ccc;
+ margin-top: 20px;
+ padding: 20px 0 80px;
+ text-align: center; }
+ .actions h3 {
+ font-size: 20px;
+ font-weight: 600;
+ line-height: 24px;
+ margin: 0 auto;
+ width: 80%; }
+ .actions ul, .actions p, .actions li {
+ font-family: "ff-tisa-web-pro", Georgia, serif;
+ font-size: 15px;
+ line-height: 20px;
+ margin: 10px auto;
+ width: 86.667%; }
+ .actions ul {
+ list-style-type: square;
+ text-align: left; }
+ .actions ul li {
+ margin-left: 20px;
+ width: auto; }
+ .actions ul li + li {
+ margin-top: 5px; }
+ @media screen and (min-width:768px) {
+ .actions ul {
+ width: 74.286%; } }
+ .actions p {
+ margin: 20px auto; }
+ .actions .btn {
+ margin-bottom: 10px;
+ width: 93.333%; }
+ @media screen and (min-width: 320px) {
+ .actions .btn {
+ width: 95.652%; } }
+ @media screen and (min-width: 480px) {
+ .actions .btn {
+ width: 200px;
+ margin-bottom: 0; } }
+
+.thanks {
+ padding-bottom: 40px; }
+ .thanks header {
+ padding-bottom: 20px; }
+ .thanks .party-info {
+ font-family: "ff-tisa-web-pro", Georgia, serif;
+ font-size: 15px;
+ line-height: 20px; }
+ @media screen and (min-width:640px) {
+ .thanks .party-info {
+ font-size: 17px;
+ line-height: 22px;
+ margin: 0 auto;
+ width: 620px; } }
+ .thanks .party-info .link, .thanks .party-info .email {
+ padding: 10px;
+ margin: 10px; }
+ .thanks .party-info .link {
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 0;
+ padding-bottom: 20px; }
+ .thanks .party-info h3 {
+ font-family: "ff-dagny-web-pro", Helvetica, Arial, sans-serif;
+ font-weight: 600;
+ font-size: 19px;
+ line-height: 22px; }
+ .thanks .party-info p + p {
+ margin-top: 10px; }
+ .thanks .party-info .secret-url {
+ font-family: "ff-dagny-web-pro", Helvetica, Arial, sans-serif;
+ font-size: 20px;
+ line-height: 22px;
+ margin: 15px 0; }
+ @media screen and (min-width:640px) {
+ .thanks .party-info .secret-url {
+ font-size: 24px;
+ line-height: 26px; } }
+ .thanks .party-info .secret-url a {
+ display: block; }
View
9 spec/app_spec.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
+require 'capybara/rspec'
-describe ThirtyOne::App do
+describe "The web app" do
include Rack::Test::Methods
let(:app) { ThirtyOne::App }
@@ -20,11 +21,11 @@
end
end
- describe "GET /party/:id" do
+ describe "on GET /party/:id" do
it 'renders the form'
end
- describe "POST /party/:id" do
- it 'saves chanes to the party'
+ describe "on POST /party/:id" do
+ it 'saves changes to the party'
end
end
View
24 spec/party_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe ThirtyOne::Party do
+
+ before { ThirtyOne::Party.delete_all; ThirtyOne::PartyBit.delete_all }
+
+ it "generates a unique code on create" do
+ ThirtyOne::RandomPhrase.should_receive(:generate).and_return("3-saucy-calamari")
+
+ new_party = ThirtyOne::Party.new
+ new_party.name = "David Demaree"
+ new_party.email = "david@demaree.me"
+ new_party.save
+
+ new_party.unique_phrase.should be_present
+ new_party.unique_phrase.should == "3-saucy-calamari"
+ end
+
+ it "uses the unique phrase as its URL param" do
+ party = ThirtyOne::Party.create(name: "David", email: "david@demaree.me")
+ party.to_param.should == party.unique_phrase
+ end
+
+end
View
16 spec/random_phrase_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe ThirtyOne::RandomPhrase do
+
+ describe ".generate" do
+ it "generates a random phrase" do
+ ThirtyOne::RandomPhrase.stub!(:rand).and_return(0)
+ ThirtyOne::RandomPhrase.should_receive(:get_random_value).with("adjectives").and_return("stoned")
+ ThirtyOne::RandomPhrase.should_receive(:get_random_value).with("nouns").and_return("redheads")
+ # ThirtyOne::RandomPhrase.should_receive(:get_random_value).with("verbs").and_return("stripped")
+ # ThirtyOne::RandomPhrase.should_receive(:get_random_value).with("adverbs").and_return("sexily")
+ ThirtyOne::RandomPhrase.generate.should == "2-stoned-redheads"
+ end
+ end
+
+end
View
100 thirty_one.rb
@@ -20,7 +20,63 @@ def self.sprockets_environment
class Bit
def self.all
require 'yaml'
- @bits = YAML.load(ERB.new(File.read("bits.yml")).result(binding))
+ @bits = YAML.load(ERB.new(File.read("#{APP_ROOT}/db/bits.yml")).result(binding))
+ end
+ end
+
+ class RandomPhrase
+ ADJECTIVES = [
+ "cute", "dapper", "large", "small", "long", "short", "thick", "narrow",
+ "deep", "flat", "whole", "low", "high", "near", "far", "fast",
+ "quick", "slow", "early", "late", "bright", "dark", "cloudy", "warm",
+ "cool", "cold", "windy", "noisy", "loud", "quiet", "dry", "clear",
+ "hard", "soft", "heavy", "light", "strong", "weak", "tidy", "clean",
+ "dirty", "empty", "full", "close", "thirsty", "hungry", "fat", "old",
+ "fresh", "dead", "healthy", "sweet", "sour", "bitter", "salty", "good",
+ "bad", "great", "important", "useful", "expensive", "cheap", "free", "difficult",
+ "strong", "weak", "able", "free", "rich", "afraid", "brave", "fine",
+ "sad", "proud", "comfortable", "happy", "clever", "interesting", "famous", "exciting",
+ "funny", "kind", "polite", "fair", "share", "busy", "free", "lazy",
+ "lucky", "careful", "safe", "dangerous"
+ ]
+
+ # English plural nouns (all animals)
+ NOUNS = [
+ "rabbits", "badgers", "foxes", "chickens", "bats", "deer", "snakes", "hares",
+ "hedgehogs", "platypuses", "moles", "mice", "otters", "rats", "squirrels", "stoats",
+ "weasels", "crows", "doves", "ducks", "geese", "hawks", "herons", "kingfishers",
+ "owls", "peafowl", "pheasants", "pigeons", "robins", "rooks", "sparrows", "starlings",
+ "swans", "ants", "bees", "butterflies", "dragonflies", "flies", "moths", "spiders",
+ "pikes", "salmons", "trouts", "frogs", "newts", "toads", "crabs", "lobsters",
+ "clams", "cockles", "mussels", "oysters", "snails", "cattle", "dogs", "donkeys",
+ "goats", "horses", "pigs", "sheep", "ferrets", "gerbils", "guinea pigs", "parrots",
+ "monkeys", "gorillas", "eagles", "lions", "leopards", "tigers", "panthers", "jaguars",
+ "bears"
+ ]
+
+ VERBS = [
+ "sang", "played", "knitted", "floundered", "danced", "played", "listened", "ran",
+ "talked", "cuddled", "sat", "kissed", "hugged", "whimpered", "hid", "fought",
+ "whispered", "cried", "snuggled", "walked", "drove", "loitered", "whimpered", "felt",
+ "jumped", "hopped", "went", "married", "engaged"
+ ]
+
+ ADVERBS = [
+ "jovially", "merrily", "cordially", "easily"
+ ]
+
+ def self.generate
+ [].tap do |arr|
+ arr << (rand(97)+2).to_s
+ %w(adjectives nouns).each do |coll|
+ arr << get_random_value(coll)
+ end
+ end.join("-")
+ end
+
+ def self.get_random_value(collection)
+ const = "#{self}::#{collection.to_s.upcase}".constantize
+ const[rand(const.length)]
end
end
@@ -45,6 +101,10 @@ def bits=(bit_list)
@bits = bit_list
end
+ def to_param
+ unique_phrase || id
+ end
+
# Need to save bits after save
after_save :update_bits
def update_bits
@@ -55,6 +115,12 @@ def update_bits
end
end
end
+
+ before_create :generate_unique_phrase
+ def generate_unique_phrase
+ self.unique_phrase ||= ThirtyOne::RandomPhrase.generate
+ end
+
end
class PartyBit < Model
@@ -62,6 +128,8 @@ class PartyBit < Model
end
class App < ::Sinatra::Base
+ enable :sessions
+
helpers do
def datetime_key_path(date, hour=nil)
"".tap do |output|
@@ -96,32 +164,48 @@ def bit_to_string(namespace, value)
post '/parties' do
@party = Party.new params[:party]
if @party.save
- redirect to("/parties/#{@party.id}")
+ redirect to("/thanks/#{@party.unique_phrase}")
else
erb :index
end
end
- post '/parties/:id' do |id|
- @party = Party.find(id)
+ post '/party/:id' do |id|
+ @party = Party.find_by_unique_phrase(id) || Party.find(id)
if @party.update_attributes(params[:party])
- redirect to("/parties/#{@party.id}")
+ session[:secret_party_id] = @party.unique_phrase
+ redirect to("/party/#{@party.unique_phrase}")
else
erb :index
end
end
- get '/parties/:id' do |id|
- @party = Party.find(id)
+ get '/party/:id' do |id|
+ @party = Party.find_by_unique_phrase(id) || Party.find(id)
+ session[:secret_party_id] = @party.unique_phrase
erb :index
end
+
+ get '/thanks/:party_id' do |party_id|
+ @party = Party.find_by_unique_phrase!(party_id)
+ session[:secret_party_id] = @party.unique_phrase
+ erb :thanks
+ end
- get '/' do
+ get '/new_party' do
params['party'] ||= {}
params['party']['email'] ||= params['email']
@party = Party.new(params['party'])
erb :index
end
+ get '/' do
+ if session[:secret_party_id]
+ redirect to("/party/#{session[:secret_party_id]}")
+ else
+ redirect to("/new_party")
+ end
+ end
+
end
end
View
0  tmp/.gitkeep
No changes.
View
300 views/index.erb
@@ -1,178 +1,168 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <title>31 on demaree•me</title>
- <link href="<%= asset_path "thirty_one.css" %>" rel="stylesheet">
- <script type="text/javascript" src="http://use.typekit.com/<%= @kit_id ||= "mcj6sta" %>.js"></script>
- <script type="text/javascript">try{Typekit.load();}catch(e){}</script>
- <script src="<%= asset_path("application.js") %>" type="text/javascript" charset="utf-8"></script>
- <meta name="viewport" content="width=device-width">
- </head>
- <body>
- <div class="container">
- <header class="banner">
- <div class="big-logo">
- <img src="http://placehold.it/150x150">
+<div class="party-form">
+ <header class="banner">
+ <div class="big-logo">
+ <!-- <img src="http://placehold.it/150x150"> -->
+ <img src="<%= asset_path "disc-200px.png" %>">
+ </div>
+ <h1>Come celebrate 31 years<br> of social awkwardness.</h1>
+ <p>
+ Against all odds and any semblance of logic, I, <a href="http://twitter.com/ddemaree">David Demaree</a>, will soon be a year older. I know, right? We should have a party or something, to reflect on this situation.
+ </p>
+ <p>
+ If you're interested in coming, in order to schedule and plan this party I will need some information from you. Please fill out the form below precisely as directed, then press the big, shiny submit button at the bottom.
+ </p>
+ </header>
+
+ <form action="<%= @party.persisted? ? url("/party/#{@party.unique_phrase}") : url("/parties") %>" method="post">
+ <section class="section row email-section">
+ <div class="span4 offset1 section-info">
+ <h2>Who are you, again?</h2>
+ <p class="section-notes">I'm kidding&mdash;<em>of course</em> I know who you are. Unfortunately the computer does not. Please type in your name and email address, and mark yourself in the books of history as "interested in this party."</p>
+ </div>
+ <div class="section-body">
+
+ <div class="field" id="party-name">
+ <input type="text" name="party[name]" class="xlarge span7" placeholder="Gene Simmons" value="<%= @party.name %>">
+ <p class="help-block">This field is <strong>required</strong>. Cookies <strong>are</strong> delicious.</p>
</div>
- <h1>Come celebrate 31 years<br> of social awkwardness.</h1>
- <p>
- Against all odds and any semblance of logic, I, <a href="http://twitter.com/ddemaree">David Demaree</a>, will soon be a year older. I know, right? We should have a party or something, to reflect on this situation.
- </p>
- <p>
- If you're interested in coming, in order to schedule and plan this party I will need some information from you. Please fill out the form below precisely as directed, then press the big, shiny submit button at the bottom.
- </p>
- </header>
+
+ <div class="field" id="party-email">
+ <input type="text" name="party[email]" class="xlarge span7" placeholder="gene@kissarmy.net" value="<%= @party.email %>">
+ <p class="help-block">This field is <strong>required</strong>. It <strong>must</strong> be a valid email address. The European debt crisis <strong>is</strong> complicated.</p>
+ </div>
+
+ <!-- <p class="section-notes">An added bonus: Further spam related to this birthday will be sent <em>only</em> to the email address you provide here.</p> -->
+
+ </div>
+ </section>
- <form action="<%= @party.persisted? ? url("/parties/#{@party.id}") : url("/parties") %>" method="post">
- <section class="section row email-section">
- <div class="span4 offset1 section-info">
- <h2>Who are you, again?</h2>
- <p class="section-notes">I'm kidding&mdash;<em>of course</em> I know who you are. Unfortunately the computer does not. Please type in your name and email address, and mark yourself in the books of history as "interested in this party."</p>
- </div>
- <div class="section-body">
-
- <div class="field" id="party-name">
- <input type="text" name="party[name]" class="xlarge span7" placeholder="Gene Simmons" value="">
- <p class="help-block">This field is <strong>required</strong>. Cookies <strong>are</strong> delicious.</p>
- </div>
-
- <div class="field" id="party-email">
- <input type="text" name="party[email]" class="xlarge span7" placeholder="gene@kissarmy.net" value="<%= @party.email %>">
- <p class="help-block">This field is <strong>required</strong>. It <strong>must</strong> be a valid email address. The European debt crisis <strong>is</strong> complicated.</p>
+ <section class="section row days-section">
+ <div class="span4 offset1 section-info">
+ <h2>When are you free?</h2>
+ <p class="section-notes">Check all the times you&rsquo;re available to par-tay. If you&rsquo;re not sure feel free to leave a box unchecked. You can also come back and change your answers later when you're more sure.</p>
+ <p>You <strong>must</strong> choose at least one time.</p>
+ </div>
+
+ <div class="section-body">
+ <div class="answer-area">
+ <% @days.each do |day| %>
+ <div class="day">
+ <div class="day-header">
+ <h3>
+ <span class="day-of-week"><%= day.strftime("%A") %></span>
+ <time datetime="<%= day.strftime("%Y-%m-%d") %>"><%= day.strftime("%m/%e").gsub(/ /,"") %></time>
+ </h3>
+ <a href="#" data-bind="
+ html: toggleLabel('<%= datetime_key_path(day) %>'),
+ click: function(){
+ toggleAll('<%= datetime_key_path(day) %>')
+ }">
+ Toggle
+ </a>
+ </div>
+ <ul>
+ <% [6,8,10].each do |hour| %>
+ <li>
+ <label data-bind="css: { 'checked': (bits().indexOf('<%= datetime_key_path(day, hour) %>') > -1) }">
+ <input type="checkbox" name="party[bits][]" value="<%= datetime_key_path(day, hour) %>" data-bind="checked: bits" <%= 'checked' if @party.bits.include?(datetime_key_path(day, hour)) %>>
+ <span class="label-text"><%= time_range(hour) %></span>
+ </label>
+ </li>
+ <% end %>
+ </ul>
</div>
-
- <!-- <p class="section-notes">An added bonus: Further spam related to this birthday will be sent <em>only</em> to the email address you provide here.</p> -->
-
- </div>
- </section>
+ <% end %>
+ </div>
+ </div>
- <section class="section row days-section">
- <div class="span4 offset1 section-info">
- <h2>When are you free?</h2>
- <p class="section-notes">Check all the times you&rsquo;re available to par-tay. If you&rsquo;re not sure feel free to leave a box unchecked. You can also come back and change your answers later when you're more sure.</p>
- <p>You <strong>must</strong> choose at least one time.</p>
- </div>
+ </section>
+
+ <section class="section row places-section">
+ <div class="span4 offset1 section-info">
+ <h2>What sounds good?</h2>
+ <p class="section-notes">
+ Please check any of the places or activities you're interested in. There is also a write-in area at the bottom, to suggest places/activities that aren't listed. This is not <em>voting</em> so much as a way of knowing before I invite everyone to (for example) a crazy molecular cocktail bar or Michelin-starred pig-eteria whether that kind of thing will delight or disgust my friends.</p>
+ </div>
- <div class="section-body">
- <div class="answer-area">
- <% @days.each do |day| %>
- <div class="day">
- <div class="day-header">
- <h3>
- <span class="day-of-week"><%= day.strftime("%A") %></span>
- <time datetime="<%= day.strftime("%Y-%m-%d") %>"><%= day.strftime("%m/%e").gsub(/ /,"") %></time>
- </h3>
+ <div class="span10 section-body">
+ <div class="places-list">
+ <div class="checkbox-sections">
+
+ <% %w(dinner drinks activities).each do |namespace| %>
+ <div id="<%= namespace %>-section" class="places-list-section">
+ <div class="places-list-header">
+ <h3>
+ <%= namespace.capitalize %>
<a href="#" data-bind="
- html: toggleLabel('<%= datetime_key_path(day) %>'),
+ html: toggleLabel('<%= namespace %>'),
click: function(){
- toggleAll('<%= datetime_key_path(day) %>')
+ toggleAll('<%= namespace %>')
}">
Toggle
</a>
- </div>
- <ul>
- <% [6,8,10].each do |hour| %>
+ </h3>
+ </div>
+ <ul class="unstyled places">
+ <% @bits[namespace].each do |key, name| %>
<li>
- <label data-bind="css: { 'checked': (bits().indexOf('<%= datetime_key_path(day, hour) %>') > -1) }">
- <input type="checkbox" name="party[bits][]" value="<%= datetime_key_path(day, hour) %>" data-bind="checked: bits" <%= 'checked' if @party.bits.include?(datetime_key_path(day, hour)) %>>
- <span class="label-text"><%= time_range(hour) %></span>
+ <label data-bind="css: { 'checked': (bits().indexOf('<%= namespace %>.<%= key %>') > -1) }">
+ <input name="party[bits][]" type="checkbox" value="<%= namespace %>.<%= key %>" data-bind="checked: bits" <%= 'checked' if @party.bits.include?("#{namespace}.#{key}") %>>
+ <span class="label-text"><%= name.gsub('&', '<span class="amp">&amp;</span>') %></span>
</label>
</li>
- <% end %>
- </ul>
- </div>
- <% end %>
- </div>
- </div>
-
- </section>
-
- <section class="section row places-section">
- <div class="span4 offset1 section-info">
- <h2>What sounds good?</h2>
- <p class="section-notes">
- Please check any of the places or activities you're interested in. There is also a write-in area at the bottom, to suggest places/activities that aren't listed. This is not <em>voting</em> so much as a way of knowing before I invite everyone to (for example) a crazy molecular cocktail bar or Michelin-starred pig-eteria whether that kind of thing will delight or disgust my friends.</p>
- </div>
-
- <div class="span10 section-body">
- <div class="places-list">
- <div class="checkbox-sections">
-
- <% %w(dinner drinks activities).each do |namespace| %>
- <div id="<%= namespace %>-section" class="places-list-section">
- <div class="places-list-header">
- <h3>
- <%= namespace.capitalize %>
- <a href="#" data-bind="
- html: toggleLabel('<%= namespace %>'),
- click: function(){
- toggleAll('<%= namespace %>')
- }">
- Toggle
- </a>
- </h3>
- </div>
- <ul class="unstyled places">
- <% @bits[namespace].each do |key, name| %>
- <li>
- <label data-bind="css: { 'checked': (bits().indexOf('<%= namespace %>.<%= key %>') > -1) }">
- <input name="party[bits][]" type="checkbox" value="<%= namespace %>.<%= key %>" data-bind="checked: bits" <%= 'checked' if @party.bits.include?("#{namespace}.#{key}") %>>
- <span class="label-text"><%= name.gsub('&', '<span class="amp">&amp;</span>') %></span>
- </label>
- </li>
- <% end %>
- </ul>
- </div>
- <% end %>
-
+ <% end %>
+ </ul>
</div>
+ <% end %>
- <div class="places-list-section notes-section">
- <div class="places-list-header">
- <h3>Other ideas?</h3>
- </div>
- <div class="notes">
- <textarea name="party[notes]" class="span8" rows="8"><%= @party.notes %></textarea>
- </div>
- </div>
+ </div>
+ <div class="places-list-section notes-section">
+ <div class="places-list-header">
+ <h3>Other ideas?</h3>
+ </div>
+ <div class="notes">
+ <textarea name="party[notes]" class="span8" rows="8"><%= @party.notes %></textarea>
</div>
</div>
- </section>
- <div class="actions">
- <h3>Please note before clicking one of these shiny buttons:</h3>
- <ul>
- <li>
- By submitting this form, you are expressing a <em>desire</em>, perhaps even an <em>intent</em>, to attend a party, shindig, hootenanny, gathering, or whatever in celebration of David&rsquo;s 31st birthday.
- </li>
- <li>
- Which implies that you will be in Chicago, Illinois, U.S.A. on one or more of the dates/times checked above. (Sorry, we will not be offering a party conference call, and television coverage of the party will be limited.)
- </li>
- </ul>
- <p>
- <strong>Still game?</strong> Awesome. Please click the button below and I'll be in touch.
- </p>
-
- <button type="submit" class="btn primary">Save the date</button>
- <button type="button" class="btn info">WTF?</button>
</div>
- </form>
+ </div>
+ </section>
+ <div class="actions">
+ <h3>Please note before clicking one of these shiny buttons:</h3>
+ <ul>
+ <li>
+ By submitting this form, you are expressing a <em>desire</em>, perhaps even an <em>intent</em>, to attend a party, shindig, hootenanny, gathering, or whatever in celebration of David&rsquo;s 31st birthday.
+ </li>
+ <li>
+ Which implies that you will be in Chicago, Illinois, U.S.A. on one or more of the dates/times checked above. (Sorry, we will not be offering a party conference call, and television coverage of the party will be limited.)
+ </li>
+ </ul>
+ <p>
+ <strong>Still game?</strong> Awesome. Please click the button below and I'll be in touch.
+ </p>
+
+ <button type="submit" class="btn primary">Save the date</button>
+ <!-- <button type="button" class="btn info">WTF?</button> -->
</div>
+ </form>
- <%# All of the JSON %>
- <script type="application/json" id="bits_json">
- <%=
- @bits.inject({}) { |hash, row|
- namespace, rows = *row
- hash[namespace] = rows.map {|p| "#{namespace}.#{p.first}" }
- hash
- }.to_json
- %>
- </script>
- <script type="application/json" id="party_json">
- <%= @party.bits.to_json %>
- </script>
- </body>
-</html>
+ <script type="application/json" id="bits_json">
+ <%=
+ @bits.inject({}) { |hash, row|
+ namespace, rows = *row
+ hash[namespace] = rows.map {|p| "#{namespace}.#{p.first}" }
+ hash
+ }.to_json
+ %>
+ </script>
+ <script type="application/json" id="party_json">
+ <%= @party.bits.to_json %>
+ </script>
+ <script type="text/javascript">
+ PartyView.init();
+ </script>
+</div>
View
33 views/layout.erb
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>31 on demaree•me</title>
+ <link href="<%= asset_path "thirty_one.css" %>" rel="stylesheet">
+ <script type="text/javascript" src="http://use.typekit.com/<%= @kit_id ||= "mcj6sta" %>.js"></script>
+ <script type="text/javascript">try{Typekit.load();}catch(e){}</script>
+ <script src="<%= asset_path("application.js") %>" type="text/javascript" charset="utf-8"></script>
+ <meta name="viewport" content="width=device-width">
+ </head>
+ <body>
+ <div class="container">
+ <%= yield %>
+ </div>
+
+ <% if ENV["RACK_ENV"] == 'production' %>
+ <script type="text/javascript">
+ var _gauges = _gauges || [];
+ (function() {
+ var t = document.createElement('script');
+ t.type = 'text/javascript';
+ t.async = true;
+ t.id = 'gauges-tracker';
+ t.setAttribute('data-site-id', '4eb5a374f5a1f54e10000001');
+ t.src = '//secure.gaug.es/track.js';
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(t, s);
+ })();
+ </script>
+ <% end %>
+ </body>
+</html>
View
25 views/thanks.erb
@@ -0,0 +1,25 @@
+<div class="thanks">
+ <header class="banner">
+ <div class="big-logo">
+ <img src="<%= asset_path "disc-200px.png" %>">
+ </div>
+ <h1>Thanks!</h1>
+ <p>
+ Your responses have been incorporated into my Party Planning Decision Matrix&trade;. Once it has finished tabulating, calculating, and vituperating, I'll be in touch to let you know when and where the party's at.
+ </p>
+ </header>
+ <div class="party-info">
+ <div class="link">
+ <h3>Your Private URL</h3>
+ <p>If you need to make any changes to any of the information you just provided, just use the following address:</p>
+ <p class="secret-url">
+ <a href="<%= url("/party/#{@party.to_param}") %>">
+ <%= url("/party/#{@party.to_param}") %>
+ </a>
+ </p>
+ <p>This is a special, secret URL; I made it just for you. (Awww!)</p>
+ </div>
+
+ <p class="email">Questions or problems? Please email <a href="mailto:david@demaree.me">david@demaree.me</a>.</p>
+ </div>
+</div>
Please sign in to comment.
Something went wrong with that request. Please try again.