Compare commits

..

55 Commits

Author SHA1 Message Date
Petko Bordjukov d6d881190a Make taks endpoint dynamic 2024-09-13 22:00:19 +03:00
Petko Bordjukov ac4bef2cac Changes for 2024 2024-09-13 20:19:25 +03:00
Petko Bordjukov 323a445967 Updates for 2023 2023-09-22 12:12:16 +03:00
Petko Bordjukov f5bd7b6d09 Update for OpenFest 2023 2023-09-18 20:03:12 +03:00
Petko Bordjukov a251ecbb52 Add longpress support on mobile 2022-09-14 11:08:11 +03:00
Petko Bordjukov 09cda9f22a Do not zoom on double tap 2022-09-14 10:59:53 +03:00
Petko Bordjukov 0be033dedf Disable selection of text in events 2022-09-14 10:11:46 +03:00
Petko Bordjukov 23b0e75872 Add og:description meta tag 2022-09-13 15:02:54 +03:00
Petko Bordjukov 7489b56395 Update splash for OF 2022 2022-09-13 14:53:45 +03:00
Petko Bordjukov e86dc6131b Update for OF 2022 2022-09-03 12:06:01 +03:00
Petko Bordjukov b4a5fd9332 Bulk update of vulnerable dependencies 2022-03-22 23:38:28 +02:00
Petko Bordjukov e23b26fbc3
Merge pull request #10 from lz1irq/add-misc-track
Add Misc track
2020-10-17 21:05:50 +03:00
Ivaylo Markov 3bf70a053c Add Misc track 2020-10-17 17:56:43 +03:00
Petko Bordjukov b2687682a4 Handle no conflicts 2020-10-16 00:16:01 +03:00
Petko Bordjukov ff701f1c83 Limit Puma version 2020-10-16 00:16:01 +03:00
Petko Bordjukov 376cb140d7 Add manifest.js 2020-10-16 00:16:01 +03:00
Petko Bordjukov 1597271475 Update for OpenFest 2020 2020-10-15 23:25:24 +03:00
Petko Bordjukov 67af3d1504 Update the bundle 2020-10-15 23:19:34 +03:00
Petko Bordjukov d1d3724916 update bundler 2020-10-15 23:18:28 +03:00
Petko Bordjukov 6e86112d0f Add opengraph and twitter metadata 2019-10-07 14:59:44 +03:00
Petko Bordjukov d97febde6d Update deployment scripts 2019-10-04 12:10:27 +03:00
Petko Bordjukov 328635a74f Update for OpenFest 2019 2019-10-04 12:04:52 +03:00
Petko Bordjukov d59b04d409 Upgrade rails 2019-10-04 11:49:32 +03:00
Petko Bordjukov d90dc20b34 Bump Rails version 2019-10-04 11:43:32 +03:00
Petko Bordjukov d6d11a85b2 Update the bundle 2019-10-04 11:42:21 +03:00
Petko Bordjukov ec4d854aae Update vulnerable dependencies 2019-01-08 00:52:02 +02:00
Petko Bordjukov 6ee488b9c9 Fix clients that have an HTTP URL left in the local storage 2018-10-04 12:31:09 +03:00
Petko Bordjukov 87bb472ccc Fix another typo 2018-10-04 12:21:59 +03:00
Petko Bordjukov 633682749d Fix a typo 2018-10-04 11:50:09 +03:00
Petko Bordjukov 772d1a5aa0 Upgrade to Rails 5.2 2018-09-25 13:19:01 +03:00
Petko Bordjukov 1c7718f248 Update for OpenFest 2018 2018-09-25 13:12:40 +03:00
Petko Bordjukov 0fba6801d5 Upgrade to Rails 5.1 2018-09-25 13:12:26 +03:00
Petko Bordjukov 3e819c7b31 Update deployment configuration 2017-10-05 07:37:38 +03:00
Petko Bordjukov 9d5ea3714d Update English translations 2017-10-05 07:37:15 +03:00
Petko Bordjukov b3c2170120
Merge remote-tracking branch 'origin/pr/2' 2017-10-05 07:32:17 +03:00
Yordan Ivanov 091b442364
Better save notifications 2017-10-04 22:39:35 +03:00
Yordan Ivanov fe365b46bf
Ignroe JetBrains IDEs 2017-09-30 21:35:42 +03:00
Petko Bordjukov 2e4801663c Fix auto submission on mobile 2017-09-28 16:59:39 +03:00
Petko Bordjukov 436f097038 Optimize db queries on talk update 2017-09-28 15:55:18 +03:00
Petko Bordjukov 579fd2c04f Fix bug when no talks are selected 2017-09-28 15:40:44 +03:00
Petko Bordjukov fc817e78b0 Force SSL in production 2017-09-25 18:51:52 +03:00
Petko Bordjukov 05b305716a Auto submit preferences on change 2017-09-25 18:48:44 +03:00
Petko Bordjukov ee346f8cc3 Update tracks for OpenFest 2017 2017-09-25 16:26:37 +03:00
Petko Bordjukov 148322368e Update for OpenFest 2017 2017-09-25 16:07:48 +03:00
Petko Bordjukov 9fb9056fed Update the bundle 2017-09-25 16:06:20 +03:00
Petko Bordjukov a8dd3ca040 Add conflicts summary to voting summary 2016-10-13 08:12:22 +03:00
Petko Bordjukov d17bc19b23 Optimize the conflicts table and summary models 2016-10-13 07:24:53 +03:00
Petko Bordjukov ea8cb4b077 Optimise conflicts calculation 2016-10-13 06:10:56 +03:00
Petko Bordjukov 1a80614471 Fix up most and last class methods 2016-10-13 04:42:18 +03:00
Petko Bordjukov a8d05ea625 Expose the most and least number of conflicts 2016-10-13 04:42:18 +03:00
Petko Bordjukov b9e6a89f22 Introduce a ConflictsForTalk model and db view 2016-10-13 04:42:18 +03:00
Petko Bordjukov 42582f79b3 Just return the array of conflicts 2016-10-12 23:18:35 +03:00
Petko Bordjukov 9d305834e3 Fix a syntax error in the conflict counting SQL 2016-10-12 21:57:30 +03:00
Petko Bordjukov 37a414328f Implement a conflicts summary model 2016-10-12 21:51:36 +03:00
Petko Bordjukov a39af296ba Implement conflicts table generation 2016-10-12 20:07:57 +03:00
74 changed files with 3837 additions and 401 deletions

4
.gitignore vendored
View File

@ -17,3 +17,7 @@
/tmp /tmp
db/schema.rb db/schema.rb
config/secrets.yml config/secrets.yml
# Ignore JetBrains IDE
/.idea
/gauge.iml

14
Capfile
View File

@ -4,6 +4,17 @@ require 'capistrano/setup'
# Include default deployment tasks # Include default deployment tasks
require 'capistrano/deploy' require 'capistrano/deploy'
# Load the SCM plugin appropriate to your project:
#
# require "capistrano/scm/hg"
# install_plugin Capistrano::SCM::Hg
# or
# require "capistrano/scm/svn"
# install_plugin Capistrano::SCM::Svn
# or
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
# Include tasks from other gems included in your Gemfile # Include tasks from other gems included in your Gemfile
# #
# For documentation on these, see for example: # For documentation on these, see for example:
@ -22,6 +33,9 @@ require 'capistrano/bundler'
require 'capistrano/rails/assets' require 'capistrano/rails/assets'
require 'capistrano/rails/migrations' require 'capistrano/rails/migrations'
require 'capistrano/puma' require 'capistrano/puma'
install_plugin Capistrano::Puma # Default puma tasks
install_plugin Capistrano::Puma::Workers # if you want to control the workers (in cluster mode)
install_plugin Capistrano::Puma::Nginx # if you want to upload a nginx site template
require 'capistrano/puma/nginx' require 'capistrano/puma/nginx'
# require 'capistrano/passenger' # require 'capistrano/passenger'

60
Dockerfile Normal file
View File

@ -0,0 +1,60 @@
# syntax = docker/dockerfile:1
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.5
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
# Rails app lives here
WORKDIR /rails
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
# Throw-away build stage to reduce size of final image
FROM base as build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libvips pkg-config libpq-dev libsqlite3-dev nodejs yarn
# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
# Copy application code
COPY . .
# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
# Final stage for app image
FROM base
# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libsqlite3-0 nodejs libpq5 libvips && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER rails:rails
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server", "--early-hints"]

18
Gemfile
View File

@ -1,28 +1,30 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '5.0.0.1' gem 'rails', '~> 7.2.1'
gem 'sqlite3' gem 'coffee-rails'
gem 'sass-rails' gem 'sass-rails'
gem 'uglifier' gem 'uglifier'
gem 'coffee-rails'
# gem 'therubyracer', platforms: :ruby # gem 'therubyracer', platforms: :ruby
gem 'jquery-rails'
gem 'jbuilder' gem 'jbuilder'
gem 'jquery-rails'
gem 'activeresource', github: 'rails/activeresource', require: 'active_resource' gem 'activeresource', github: 'rails/activeresource', require: 'active_resource'
gem 'pry-rails' gem 'pry-rails'
gem 'rack-attack'
gem 'bootsnap', require: false
# gem 'spreadsheet_architect'
group :development do group :development do
gem 'spring' gem 'spring'
gem 'capistrano-rails' gem 'sqlite3'
gem 'capistrano-rvm'
gem 'capistrano3-puma'
end end
group :production do group :production do
gem 'puma'
gem 'pg' gem 'pg'
gem 'puma'
end end

View File

@ -1,205 +1,262 @@
GIT GIT
remote: git://github.com/rails/activeresource.git remote: https://github.com/rails/activeresource.git
revision: f8abaf13174e94d179227f352c9dd6fb8b03e0da revision: a1f6a19652709f2da6aaa2559f7cd0a4f7d2cf3e
specs: specs:
activeresource (5.0.0) activeresource (6.1.1)
activemodel (> 4.2, < 6) activemodel (>= 6.0)
activemodel-serializers-xml (~> 1.0) activemodel-serializers-xml (~> 1.0)
activesupport (> 4.2, < 6) activesupport (>= 6.0)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.0.0.1) actioncable (7.2.1)
actionpack (= 5.0.0.1) actionpack (= 7.2.1)
nio4r (~> 1.2) activesupport (= 7.2.1)
websocket-driver (~> 0.6.1) nio4r (~> 2.0)
actionmailer (5.0.0.1) websocket-driver (>= 0.6.1)
actionpack (= 5.0.0.1) zeitwerk (~> 2.6)
actionview (= 5.0.0.1) actionmailbox (7.2.1)
activejob (= 5.0.0.1) actionpack (= 7.2.1)
mail (~> 2.5, >= 2.5.4) activejob (= 7.2.1)
rails-dom-testing (~> 2.0) activerecord (= 7.2.1)
actionpack (5.0.0.1) activestorage (= 7.2.1)
actionview (= 5.0.0.1) activesupport (= 7.2.1)
activesupport (= 5.0.0.1) mail (>= 2.8.0)
rack (~> 2.0) actionmailer (7.2.1)
rack-test (~> 0.6.3) actionpack (= 7.2.1)
rails-dom-testing (~> 2.0) actionview (= 7.2.1)
rails-html-sanitizer (~> 1.0, >= 1.0.2) activejob (= 7.2.1)
actionview (5.0.0.1) activesupport (= 7.2.1)
activesupport (= 5.0.0.1) mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (7.2.1)
actionview (= 7.2.1)
activesupport (= 7.2.1)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4, < 3.2)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (7.2.1)
actionpack (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.2.1)
activesupport (= 7.2.1)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubi (~> 1.11)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.6)
activejob (5.0.0.1) activejob (7.2.1)
activesupport (= 5.0.0.1) activesupport (= 7.2.1)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.0.0.1) activemodel (7.2.1)
activesupport (= 5.0.0.1) activesupport (= 7.2.1)
activemodel-serializers-xml (1.0.1) activemodel-serializers-xml (1.0.2)
activemodel (> 5.x) activemodel (> 5.x)
activerecord (> 5.x)
activesupport (> 5.x) activesupport (> 5.x)
builder (~> 3.1) builder (~> 3.1)
activerecord (5.0.0.1) activerecord (7.2.1)
activemodel (= 5.0.0.1) activemodel (= 7.2.1)
activesupport (= 5.0.0.1) activesupport (= 7.2.1)
arel (~> 7.0) timeout (>= 0.4.0)
activesupport (5.0.0.1) activestorage (7.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2) actionpack (= 7.2.1)
i18n (~> 0.7) activejob (= 7.2.1)
minitest (~> 5.1) activerecord (= 7.2.1)
tzinfo (~> 1.1) activesupport (= 7.2.1)
airbrussh (1.1.1) marcel (~> 1.0)
sshkit (>= 1.6.1, != 1.7.0) activesupport (7.2.1)
arel (7.1.2) base64
builder (3.2.2) bigdecimal
capistrano (3.6.1) concurrent-ruby (~> 1.0, >= 1.3.1)
airbrussh (>= 1.0.0) connection_pool (>= 2.2.5)
capistrano-harrow drb
i18n i18n (>= 1.6, < 2)
rake (>= 10.0.0) logger (>= 1.4.2)
sshkit (>= 1.9.0) minitest (>= 5.1)
capistrano-bundler (1.1.4) securerandom (>= 0.3)
capistrano (~> 3.1) tzinfo (~> 2.0, >= 2.0.5)
sshkit (~> 1.2) base64 (0.2.0)
capistrano-harrow (0.5.3) bigdecimal (3.1.8)
capistrano-rails (1.1.8) bootsnap (1.18.4)
capistrano (~> 3.1) msgpack (~> 1.2)
capistrano-bundler (~> 1.1) builder (3.3.0)
capistrano-rvm (0.1.2) coderay (1.1.3)
capistrano (~> 3.0) coffee-rails (5.0.0)
sshkit (~> 1.2)
capistrano3-puma (1.2.1)
capistrano (~> 3.0)
puma (>= 2.6)
coderay (1.1.1)
coffee-rails (4.2.1)
coffee-script (>= 2.2.0) coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.2.x) railties (>= 5.2.0)
coffee-script (2.4.1) coffee-script (2.4.1)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.10.0) coffee-script-source (1.12.2)
concurrent-ruby (1.0.2) concurrent-ruby (1.3.4)
erubis (2.7.0) connection_pool (2.4.1)
execjs (2.7.0) crass (1.0.6)
globalid (0.3.7) date (3.3.4)
activesupport (>= 4.1.0) drb (2.2.1)
i18n (0.7.0) erubi (1.13.0)
jbuilder (2.6.0) execjs (2.9.1)
activesupport (>= 3.0.0, < 5.1) ffi (1.17.0)
multi_json (~> 1.2) globalid (1.2.1)
jquery-rails (4.2.1) activesupport (>= 6.1)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
io-console (0.7.2)
irb (1.14.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jbuilder (2.12.0)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jquery-rails (4.6.0)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
loofah (2.0.3) logger (1.6.1)
nokogiri (>= 1.5.9) loofah (2.22.0)
mail (2.6.4) crass (~> 1.0.2)
mime-types (>= 1.16, < 4) nokogiri (>= 1.12.0)
method_source (0.8.2) mail (2.8.1)
mime-types (3.1) mini_mime (>= 0.1.1)
mime-types-data (~> 3.2015) net-imap
mime-types-data (3.2016.0521) net-pop
mini_portile2 (2.1.0) net-smtp
minitest (5.9.1) marcel (1.0.4)
multi_json (1.12.1) method_source (1.1.0)
net-scp (1.2.1) mini_mime (1.1.5)
net-ssh (>= 2.6.5) mini_portile2 (2.8.7)
net-ssh (3.2.0) minitest (5.25.1)
nio4r (1.2.1) msgpack (1.7.2)
nokogiri (1.6.8) net-imap (0.4.16)
mini_portile2 (~> 2.1.0) date
pkg-config (~> 1.1.7) net-protocol
pg (0.19.0) net-pop (0.1.2)
pkg-config (1.1.7) net-protocol
pry (0.10.4) net-protocol (0.2.2)
coderay (~> 1.1.0) timeout
method_source (~> 0.8.1) net-smtp (0.5.0)
slop (~> 3.4) net-protocol
pry-rails (0.3.4) nio4r (2.7.3)
pry (>= 0.9.10) nokogiri (1.16.7)
puma (3.6.0) mini_portile2 (~> 2.8.2)
rack (2.0.1) racc (~> 1.4)
rack-test (0.6.3) pg (1.5.8)
rack (>= 1.0) pry (0.14.2)
rails (5.0.0.1) coderay (~> 1.1)
actioncable (= 5.0.0.1) method_source (~> 1.0)
actionmailer (= 5.0.0.1) pry-rails (0.3.11)
actionpack (= 5.0.0.1) pry (>= 0.13.0)
actionview (= 5.0.0.1) psych (5.1.2)
activejob (= 5.0.0.1) stringio
activemodel (= 5.0.0.1) puma (6.4.2)
activerecord (= 5.0.0.1) nio4r (~> 2.0)
activesupport (= 5.0.0.1) racc (1.8.1)
bundler (>= 1.3.0, < 2.0) rack (3.1.7)
railties (= 5.0.0.1) rack-attack (6.7.0)
sprockets-rails (>= 2.0.0) rack (>= 1.0, < 4)
rails-dom-testing (2.0.1) rack-session (2.0.0)
activesupport (>= 4.2.0, < 6.0) rack (>= 3.0.0)
nokogiri (~> 1.6.0) rack-test (2.1.0)
rails-html-sanitizer (1.0.3) rack (>= 1.3)
loofah (~> 2.0) rackup (2.1.0)
railties (5.0.0.1) rack (>= 3)
actionpack (= 5.0.0.1) webrick (~> 1.8)
activesupport (= 5.0.0.1) rails (7.2.1)
method_source actioncable (= 7.2.1)
rake (>= 0.8.7) actionmailbox (= 7.2.1)
thor (>= 0.18.1, < 2.0) actionmailer (= 7.2.1)
rake (11.3.0) actionpack (= 7.2.1)
sass (3.4.22) actiontext (= 7.2.1)
sass-rails (5.0.6) actionview (= 7.2.1)
railties (>= 4.0.0, < 6) activejob (= 7.2.1)
sass (~> 3.1) activemodel (= 7.2.1)
sprockets (>= 2.8, < 4.0) activerecord (= 7.2.1)
sprockets-rails (>= 2.0, < 4.0) activestorage (= 7.2.1)
tilt (>= 1.1, < 3) activesupport (= 7.2.1)
slop (3.6.0) bundler (>= 1.15.0)
spring (1.7.2) railties (= 7.2.1)
sprockets (3.7.0) rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.2.1)
actionpack (= 7.2.1)
activesupport (= 7.2.1)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rake (13.2.1)
rdoc (6.7.0)
psych (>= 4.0.0)
reline (0.5.10)
io-console (~> 0.5)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
securerandom (0.3.1)
spring (4.2.1)
sprockets (4.2.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (>= 2.2.4, < 4)
sprockets-rails (3.2.0) sprockets-rails (3.5.2)
actionpack (>= 4.0) actionpack (>= 6.1)
activesupport (>= 4.0) activesupport (>= 6.1)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sqlite3 (1.3.11) sqlite3 (2.0.4)
sshkit (1.11.3) mini_portile2 (~> 2.8.0)
net-scp (>= 1.1.2) stringio (3.1.1)
net-ssh (>= 2.8.0) thor (1.3.2)
thor (0.19.1) tilt (2.4.0)
thread_safe (0.3.5) timeout (0.4.1)
tilt (2.0.5) tzinfo (2.0.6)
tzinfo (1.2.2) concurrent-ruby (~> 1.0)
thread_safe (~> 0.1) uglifier (4.2.0)
uglifier (3.0.2)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
websocket-driver (0.6.4) useragent (0.16.10)
webrick (1.8.1)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2) websocket-extensions (0.1.5)
zeitwerk (2.6.18)
PLATFORMS PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
activeresource! activeresource!
capistrano-rails bootsnap
capistrano-rvm
capistrano3-puma
coffee-rails coffee-rails
jbuilder jbuilder
jquery-rails jquery-rails
pg pg
pry-rails pry-rails
puma puma
rails (= 5.0.0.1) rack-attack
rails (~> 7.2.1)
sass-rails sass-rails
spring spring
sqlite3 sqlite3
uglifier uglifier
BUNDLED WITH BUNDLED WITH
1.12.5 2.5.16

View File

@ -0,0 +1,3 @@
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -12,4 +12,5 @@
// //
//= require jquery //= require jquery
//= require jquery_ujs //= require jquery_ujs
//= require jquery_mobile_events
//= require_tree . //= require_tree .

View File

@ -0,0 +1,5 @@
if ('OpenFest-gauge-api' in localStorage) {
if (localStorage['OpenFest-gauge-api'].startsWith('http://')) {
localStorage['OpenFest-gauge-api'] = localStorage['OpenFest-gauge-api'].replace(/^http:\/\//, 'https://');
}
}

View File

@ -1,4 +1,17 @@
(function() { (function() {
function show_status(status) {
var container = $('.status');
var span = container.find(status ? '.success' : '.failed');
container.find('span').hide();
span.show();
container.addClass('shown');
setTimeout(function() {
container.removeClass('shown');
}, 3000);
}
function toggle_grid(whichDay) { function toggle_grid(whichDay) {
var vclasses= ['in-list', 'in-calendar onlyday1', 'in-calendar onlyday2', 'in-calendar onlyday3', var vclasses= ['in-list', 'in-calendar onlyday1', 'in-calendar onlyday2', 'in-calendar onlyday3',
'in-calendar onlyday4', 'in-calendar alldays']; 'in-calendar onlyday4', 'in-calendar alldays'];
@ -47,8 +60,6 @@
if( !myapi || !myapi.length ) { if( !myapi || !myapi.length ) {
/* If we do not have resource URL, post data and get resource */ /* If we do not have resource URL, post data and get resource */
$.post( halfnarpAPI, request, function( data ) { $.post( halfnarpAPI, request, function( data ) {
$('.info span').text('submitted');
$('.info').removeClass('hidden');
try { try {
localStorage['OpenFest-gauge-api'] = data['update_url']; localStorage['OpenFest-gauge-api'] = data['update_url'];
localStorage['OpenFest-gauge-pid'] = mypid = data['hashed_uid']; localStorage['OpenFest-gauge-pid'] = mypid = data['hashed_uid'];
@ -56,8 +67,7 @@
window.location.hash = mypid; window.location.hash = mypid;
} catch(err) {} } catch(err) {}
}, 'json' ).fail(function() { }, 'json' ).fail(function() {
$('.info span').text('failed :('); show_status(false);
$('.info').removeClass('hidden');
}); });
} else { } else {
/* If we do have a resource URL, update resource */ /* If we do have a resource URL, update resource */
@ -71,11 +81,9 @@
if( localStorage['OpenFest-gauge-pid'] ) { if( localStorage['OpenFest-gauge-pid'] ) {
window.location.hash = localStorage['OpenFest-gauge-pid']; window.location.hash = localStorage['OpenFest-gauge-pid'];
} }
$('.info span').text('updated'); show_status(true);
$('.info').removeClass('hidden'); }).fail(function() {
}).fail(function(msg) { show_status(false);
$('.info span').text('failed');
$('.info').removeClass('hidden');
}); });
} }
@ -215,23 +223,33 @@
/* Apply attributes to sort events into calendar */ /* Apply attributes to sort events into calendar */
// t.addClass(' room_' + item.room_id + ' duration_' + item.duration + ' day_'+day + ' time_' + (hour<10?'0':'') + hour + '' + (mins<10?'0':'') + mins); // t.addClass(' room_' + item.room_id + ' duration_' + item.duration + ' day_'+day + ' time_' + (hour<10?'0':'') + hour + '' + (mins<10?'0':'') + mins);
t.click( function(event) {
/* Transition for touch devices is highlighted => selected => highlighted ... */
if( isTouch ) { if( isTouch ) {
t.click( function(event) {
if ( $( this ).hasClass('highlighted') ) { if ( $( this ).hasClass('highlighted') ) {
$( this ).toggleClass('selected'); $( this ).toggleClass('selected');
$('.info').addClass('hidden'); $('.info').addClass('hidden');
$('.submit').click();
} else { } else {
$('.highlighted').removeClass('highlighted'); $('.highlighted').removeClass('highlighted');
$( this ).addClass('highlighted'); $( this ).addClass('highlighted');
} }
} else {
$( this ).toggleClass('selected');
$('.info').addClass('hidden');
}
event.stopPropagation(); event.stopPropagation();
}); });
$(t).bind('taphold', function(event) {
$( this ).toggleClass('selected');
$('.info').addClass('hidden');
$('.submit').click();
});
} else {
t.click( function(event) {
$( this ).toggleClass('selected');
$('.info').addClass('hidden');
$('.submit').click();
event.stopPropagation();
});
}
/* Put new event into DOM tree. Track defaults to 'Other' */ /* Put new event into DOM tree. Track defaults to 'Other' */
var track = item.track_id.toString(); var track = item.track_id.toString();
var d = $( '#' + track ); var d = $( '#' + track );

View File

@ -14,6 +14,11 @@
*= require_self *= require_self
*/ */
.event {
user-select: none;
touch-action: manipulation;
}
.event.friend { .event.friend {
background: yellow !important; background: yellow !important;
} }

View File

@ -2,6 +2,29 @@ body {
font-family: "HelveticaNeueLight", "HelveticaNeue-Light", "Helvetica Neue Light", "HelveticaNeue", "Helvetica Neue", 'TeXGyreHerosRegular', "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif; font-weight:300; font-stretch:normal; font-family: "HelveticaNeueLight", "HelveticaNeue-Light", "Helvetica Neue Light", "HelveticaNeue", "Helvetica Neue", 'TeXGyreHerosRegular', "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif; font-weight:300; font-stretch:normal;
} }
.status {
position: fixed;
z-index: 1;
transition: opacity 0.6s;
top: 0;
left: 0;
right: 0;
opacity:0;
background: #fde073;
text-align: center;
line-height: 2.5;
overflow: hidden;
box-shadow: 0 0 5px black;
}
.status.shown {
opacity: 1;
}
.status span {
display: none;
}
.header { .header {
min-width: 640px; min-width: 640px;
} }

View File

@ -0,0 +1,11 @@
class ConflictsController < ApplicationController
def show
@conflicts_table = ConflictsTable.new conflicts_table_params
end
private
def conflicts_table_params
params.require(:conflicts_table).permit(:talk_id, other_talks_ids: [])
end
end

View File

@ -0,0 +1,11 @@
class ConflictsSummariesController < ApplicationController
def show
@conflicts_summary = ConflictsSummary.new conflicts_summary_params
end
private
def conflicts_summary_params
params.require(:conflicts_summary).permit(talk_ids: [])
end
end

View File

@ -26,10 +26,10 @@ class TalkPreferencesController < ApplicationController
def update def update
@talk_preference = TalkPreference.find params[:id] @talk_preference = TalkPreference.find params[:id]
@talk_preference.transaction do @talk_preference.with_lock do
@talk_preference.selected_talks.destroy_all SelectedTalk.where(talk_preference_id: @talk_preference.id).delete_all
if @talk_preference.update talk_preference_params if params[:talk_preference].blank? || @talk_preference.update(talk_preference_params)
render json: { render json: {
update_url: talk_preference_url(@talk_preference), update_url: talk_preference_url(@talk_preference),
hashed_uid: @talk_preference.hashed_unique_id, hashed_uid: @talk_preference.hashed_unique_id,

9
app/models/conflicts.rb Normal file
View File

@ -0,0 +1,9 @@
class Conflicts < ApplicationRecord
def self.most
order(conflicts: :desc).try(:first)
end
def self.least
order(conflicts: :asc).try(:first)
end
end

View File

@ -0,0 +1,9 @@
class ConflictsForTalk < ApplicationRecord
def self.most
order(conflicts: :desc).first
end
def self.least
order(conflicts: :asc).first
end
end

View File

@ -0,0 +1,44 @@
class ConflictsSummary
include ActiveModel::Model
attr_accessor :talk_ids
def talk_ids
@talk_ids ||= []
end
def conflicts
talk_ids.map do |talk_id|
{
talk_id: talk_id,
conflicts: conflicts_table[talk_id]
}
end
end
private
def conflicts_table
@conflicts_table ||= blank_conflicts_table.merge(database_conflicts_table)
end
def database_conflicts_table
Conflicts.where(left: talk_ids, right: talk_ids).group_by(&:left).map do |left, conflicts|
conflicts_row = blank_conflicts_row(talk_ids_without(left))
conflicts_row.merge! conflicts.map { |right_conflicts| [right_conflicts.right, right_conflicts.conflicts] }.to_h
[left, conflicts_row]
end.to_h
end
def talk_ids_without(talk_id)
talk_ids.reject { |id| id == talk_id }
end
def blank_conflicts_row(other_talk_ids)
other_talk_ids.map { |talk_id| [talk_id, 0] }.to_h
end
def blank_conflicts_table
talk_ids.map { |talk_id| [talk_id, blank_conflicts_row(talk_ids_without(talk_id))] }.to_h
end
end

View File

@ -0,0 +1,26 @@
class ConflictsTable
include ActiveModel::Model
attr_accessor :talk_id, :other_talks_ids
def other_talks_ids
@other_talks_ids ||= []
end
def conflicts
other_talks_ids.map do |right|
{
talk_id: right,
number_of_conflicts: conflicts_hash[right] || 0
}
end
end
private
def conflicts_hash
@conflicts_hash ||= Conflicts.where(left: talk_id, right: other_talks_ids).map do |conflicts|
[conflicts.right, conflicts.conflicts]
end.to_h
end
end

View File

@ -9,7 +9,33 @@ class Summary
.uniq.count .uniq.count
end end
def most_conflicts
Conflicts.where(left: talk_ids, right: talk_ids).most&.conflicts || 0
end
def least_conflicts
if least_conflicts_for_single_talk == 0
0
else
least_conflicts_between_two_talks
end
end
def ranking def ranking
@ranking ||= Ranking.new(talk_ids: talk_ids).ranking @ranking ||= Ranking.new(talk_ids: talk_ids).ranking
end end
def conflicts
@conflicts ||= ConflictsSummary.new(talk_ids: talk_ids).conflicts
end
private
def least_conflicts_for_single_talk
ConflictsForTalk.where(talk_id: talk_ids).least&.conflicts || 0
end
def least_conflicts_between_two_talks
Conflicts.where(left: talk_ids, right: talk_ids).least&.conflicts || 0
end
end end

View File

@ -1,8 +1,8 @@
class Talk < ActiveResource::Base class Talk < ActiveResource::Base
has_many :selections, class_name: 'SelectedTalk' has_many :selections, class_name: 'SelectedTalk'
self.site = "https://cfp.openfest.org/api/conferences/3" self.site = ENV['GAUGE_TALKS_ENDPOINT'] || 'https://cfp.openfest.org/api/conferences/1'
self.element_name = "event" self.element_name = 'event'
def self.ordered_by_id def self.ordered_by_id
find(:all, from: :halfnarp_friendly).sort_by(&:id) find(:all, from: :halfnarp_friendly).sort_by(&:id)

View File

@ -0,0 +1 @@
json.merge! @conflicts_table.conflicts

View File

@ -0,0 +1 @@
json.merge! @conflicts_summary.conflicts

View File

@ -1,3 +1,8 @@
<div class="status">
<span class="success"><%= t 'generic.status_success' %></span>
<span class="failed"><%= t 'generic.status_failed' %></span>
</div>
<div style="display:none"> <div style="display:none">
<div id="template"> <div id="template">
<div class="title"></div> <div class="title"></div>
@ -67,10 +72,7 @@
<div class="wholeday room1 day_1 guide"></div> <div class="wholeday room1 day_1 guide"></div>
<div class="wholeday room1 day_3 guide"></div> <div class="wholeday room1 day_3 guide"></div>
<div class="track" id="17"><h2>Technical</h2><div id="qrcode" class="hidden"></div></div> <div class="track" id="80"><h2>Technical</h2><div id="qrcode" class="hidden"></div></div>
<div class="track" id="22"><h2>OpenBiz</h2></div> <div class="track" id="79"><h2>Advanced Technical</h2></div>
<div class="track" id="18"><h2>Civic Hacking</h2></div> <div class="track" id="81"><h2>OpenArt</h2></div>
<div class="track" id="19"><h2>Social</h2></div> <div class="track" id="82"><h2>Community/Social</h2></div>
<div class="track" id="23"><h2>OpenArt</h2></div>
<div class="track" id="20"><h2>Advanced Technical</h2></div>
<div class="track" id="24"><h2>Education</h2></div>

View File

@ -5,6 +5,17 @@
<%= stylesheet_link_tag 'application', media: 'all' %> <%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %> <%= javascript_include_tag 'application' %>
<%= csrf_meta_tags %> <%= csrf_meta_tags %>
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@openfestbg">
<meta name="twitter:title" content="<%= t 'generic.title' %>">
<meta name="twitter:description" content="<%= t 'generic.help_us_reduce_the_conflicts' %>">
<meta name="twitter:image" content="<%= image_url 'openfest-splash.webp' %>">
<meta property="og:type" content="website">
<meta property="og:url" content="https://vote.openfest.org/" />
<meta property="og:title" content="<%= t 'generic.title' %>">
<meta property="og:image" content="<%= image_url 'openfest-splash.webp' %>">
<meta property="og:description" content="<%= t 'generic.help_us_reduce_the_conflicts' %>">
</head> </head>
<body class="size-small in-list halfnarp"> <body class="size-small in-list halfnarp">

View File

@ -1 +1 @@
json.extract! @summary, :number_of_ballots, :ranking json.extract! @summary, :number_of_ballots, :most_conflicts, :least_conflicts, :ranking, :conflicts

View File

@ -1,3 +1,3 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
load Gem.bin_path('bundler', 'bundle') load Gem.bin_path('bundler', 'bundle')

8
bin/docker-entrypoint Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash -e
# If running the rails server then create or migrate existing database
if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then
./bin/rails db:prepare
fi
exec "${@}"

View File

@ -1,4 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
APP_PATH = File.expand_path('../config/application', __dir__) APP_PATH = File.expand_path("../config/application", __dir__)
require_relative '../config/boot' require_relative "../config/boot"
require 'rails/commands' require "rails/commands"

View File

@ -1,4 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require_relative '../config/boot' require_relative "../config/boot"
require 'rake' require "rake"
Rake.application.run Rake.application.run

View File

@ -1,34 +1,37 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'pathname' require "fileutils"
require 'fileutils'
include FileUtils
# path to your application root. APP_ROOT = File.expand_path("..", __dir__)
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) APP_NAME = "gauge"
def system!(*args) def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==") system(*args, exception: true)
end end
chdir APP_ROOT do FileUtils.chdir APP_ROOT do
# This script is a starting point to setup your application. # This script is a way to set up or update your development environment automatically.
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
# Add necessary setup steps to this file. # Add necessary setup steps to this file.
puts '== Installing dependencies ==' puts "== Installing dependencies =="
system! 'gem install bundler --conservative' system! "gem install bundler --conservative"
system('bundle check') || system!('bundle install') system("bundle check") || system!("bundle install")
# puts "\n== Copying sample files ==" # puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml') # unless File.exist?("config/database.yml")
# cp 'config/database.yml.sample', 'config/database.yml' # FileUtils.cp "config/database.yml.sample", "config/database.yml"
# end # end
puts "\n== Preparing database ==" puts "\n== Preparing database =="
system! 'bin/rails db:setup' system! "bin/rails db:prepare"
puts "\n== Removing old logs and tempfiles ==" puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear' system! "bin/rails log:clear tmp:clear"
puts "\n== Restarting application server ==" puts "\n== Restarting application server =="
system! 'bin/rails restart' system! "bin/rails restart"
# puts "\n== Configuring puma-dev =="
# system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}"
# system "curl -Is https://#{APP_NAME}.test/up | head -n 1"
end end

View File

@ -1,10 +1,9 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'pathname'
require 'fileutils' require 'fileutils'
include FileUtils include FileUtils
# path to your application root. # path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) APP_ROOT = File.expand_path('..', __dir__)
def system!(*args) def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==") system(*args) || abort("\n== Command #{args} failed ==")
@ -18,6 +17,9 @@ chdir APP_ROOT do
system! 'gem install bundler --conservative' system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install') system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
puts "\n== Updating database ==" puts "\n== Updating database =="
system! 'bin/rails db:migrate' system! 'bin/rails db:migrate'

11
bin/yarn Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env ruby
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
begin
exec "yarnpkg", *ARGV
rescue Errno::ENOENT
$stderr.puts "Yarn executable was not detected in the system."
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1
end
end

View File

@ -8,12 +8,25 @@ Bundler.require(*Rails.groups)
module Gauge module Gauge
class Application < Rails::Application class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here. # Initialize configuration defaults for originally generated Rails version.
# Application configuration should go into files in config/initializers config.load_defaults 7.2
# -- all .rb files in that directory are automatically loaded.
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
config.i18n.available_locales = [:bg, :en] config.i18n.available_locales = %i[bg en]
config.i18n.default_locale = :bg config.i18n.default_locale = :bg
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets tasks])
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
end end
end end

View File

@ -1,3 +1,4 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile. require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup'

View File

@ -2,8 +2,9 @@ development:
adapter: async adapter: async
test: test:
adapter: async adapter: test
production: production:
adapter: redis adapter: redis
url: redis://localhost:6379/1 url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: gauge_production

View File

@ -21,5 +21,10 @@ test:
database: db/test.sqlite3 database: db/test.sqlite3
production: production:
<<: *default adapter: postgresql
database: db/production.sqlite3 encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV.fetch("GAUGE_DATABASE_HOST") { "host.containers.internal" } %>
database: gauge
username: gauge
password: <%= ENV["GAUGE_DATABASE_PASSWORD"] %>

View File

@ -1,5 +1,5 @@
# config valid only for current version of Capistrano # config valid only for current version of Capistrano
lock '3.6.1' lock '3.17.1'
set :application, 'gauge' set :application, 'gauge'
set :repo_url, 'https://github.com/OpenFest/gauge.git' set :repo_url, 'https://github.com/OpenFest/gauge.git'
@ -33,15 +33,17 @@ append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bund
# set :default_env, { path: "/opt/ruby/bin:$PATH" } # set :default_env, { path: "/opt/ruby/bin:$PATH" }
# Default value for keep_releases is 5 # Default value for keep_releases is 5
# set :keep_releases, 5 set :keep_releases, 50
set :rvm_ruby_version, '2.3.1' set :rvm_ruby_version, '2.6.5'
set :puma_bind, ["tcp://127.0.0.1:9088"] set :puma_bind, ["tcp://127.0.0.1:9088"]
set :puma_init_active_record, true set :puma_init_active_record, true
set :puma_access_log, "#{shared_path}/log/puma_access.log" set :puma_access_log, "#{shared_path}/log/puma_access.log"
set :puma_error_log, "#{shared_path}/log/puma_error.log" set :puma_error_log, "#{shared_path}/log/puma_error.log"
set :puma_preload_app, true set :puma_preload_app, true
set :puma_threads, [0, 16]
set :puma_workers, 0
set :nginx_sites_available_path, "#{shared_path}" set :nginx_sites_available_path, "#{shared_path}"
set :nginx_sites_enabled_path, "/tmp" set :nginx_sites_enabled_path, "/tmp"

View File

@ -1,5 +1,5 @@
# Load the Rails application. # Load the Rails application.
require_relative 'application' require_relative "application"
# Initialize the Rails application. # Initialize the Rails application.
Rails.application.initialize! Rails.application.initialize!

View File

@ -1,10 +1,12 @@
require "active_support/core_ext/integer/time"
Rails.application.configure do Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on # In the development environment your application's code is reloaded any time
# every request. This slows down response time but is perfect for development # it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes. # since you don't have to restart the web server when you make code changes.
config.cache_classes = false config.enable_reloading = true
# Do not eager load code on boot. # Do not eager load code on boot.
config.eager_load = false config.eager_load = false
@ -12,43 +14,65 @@ Rails.application.configure do
# Show full error reports. # Show full error reports.
config.consider_all_requests_local = true config.consider_all_requests_local = true
# Enable server timing.
config.server_timing = true
# Enable/disable caching. By default caching is disabled. # Enable/disable caching. By default caching is disabled.
if Rails.root.join('tmp/caching-dev.txt').exist? # Run rails dev:cache to toggle caching.
if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :memory_store config.cache_store = :memory_store
config.public_file_server.headers = { config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
'Cache-Control' => 'public, max-age=172800'
}
else else
config.action_controller.perform_caching = false config.action_controller.perform_caching = false
config.cache_store = :null_store config.cache_store = :null_store
end end
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Don't care if the mailer can't send. # Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false config.action_mailer.raise_delivery_errors = false
# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
# Print deprecation notices to the Rails logger. # Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log config.active_support.deprecation = :log
# Raise exceptions for disallowed deprecations.
config.active_support.disallowed_deprecation = :raise
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []
# Raise an error on page load if there are pending migrations. # Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load config.active_record.migration_error = :page_load
# Debug mode disables concatenation and preprocessing of assets. # Highlight code that triggered database queries in logs.
# This option may cause significant delays in view rendering with a large config.active_record.verbose_query_logs = true
# number of complex assets.
config.assets.debug = true # Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true
# Suppress logger output for asset requests. # Suppress logger output for asset requests.
config.assets.quiet = true config.assets.quiet = true
# Raises error for missing translations # Raises error for missing translations.
# config.action_view.raise_on_missing_translations = true # config.i18n.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code, # Annotate rendered view with file names.
# routes, locales, etc. This feature depends on the listen gem. config.action_view.annotate_rendered_view_with_filenames = true
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
end end

View File

@ -1,8 +1,10 @@
require "active_support/core_ext/integer/time"
Rails.application.configure do Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests. # Code is not reloaded between requests.
config.cache_classes = true config.enable_reloading = false
# Eager load code on boot. This eager loads most of Rails and # Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers # your application in memory, allowing both threaded web servers
@ -14,47 +16,66 @@ Rails.application.configure do
config.consider_all_requests_local = false config.consider_all_requests_local = false
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
# Disable serving static files from the `/public` folder by default since # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
# Apache or NGINX already handles this. # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # config.require_master_key = true
# Compress JavaScripts and CSS. # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
config.assets.js_compressor = :uglifier # config.public_file_server.enabled = false
# Compress CSS using a preprocessor.
# config.assets.css_compressor = :sass # config.assets.css_compressor = :sass
# Do not fall back to assets pipeline if a precompiled asset is missed. # Do not fall back to assets pipeline if a precompiled asset is missed.
config.assets.compile = false config.assets.compile = false
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
# Enable serving of images, stylesheets, and JavaScripts from an asset server. # Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com' # config.asset_host = "http://assets.example.com"
# Specifies the header that your server uses for sending files. # Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
# Mount Action Cable outside main process or domain # Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil # config.action_cable.mount_path = nil
# config.action_cable.url = 'wss://example.com/cable' # config.action_cable.url = "wss://example.com/cable"
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
# Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
# config.assume_ssl = true
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information # Skip http-to-https redirect for the default health check endpoint.
# when problems arise. # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
config.log_level = :debug
# Log to STDOUT by default
config.logger = ActiveSupport::Logger.new(STDOUT)
.tap { |logger| logger.formatter = ::Logger::Formatter.new }
.then { |logger| ActiveSupport::TaggedLogging.new(logger) }
# Prepend all log lines with the following tags. # Prepend all log lines with the following tags.
config.log_tags = [ :request_id ] config.log_tags = [ :request_id ]
# "info" includes generic and useful information about system operation, but avoids logging too much
# information to avoid inadvertent exposure of personally identifiable information (PII). If you
# want to log everything, set the level to "debug".
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
# Use a different cache store in production. # Use a different cache store in production.
# config.cache_store = :mem_cache_store # config.cache_store = :mem_cache_store
# Use a real queuing backend for Active Job (and separate queues per environment) # Use a real queuing backend for Active Job (and separate queues per environment).
# config.active_job.queue_adapter = :resque # config.active_job.queue_adapter = :resque
# config.active_job.queue_name_prefix = "gauge_#{Rails.env}" # config.active_job.queue_name_prefix = "gauge_production"
# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
# Ignore bad email addresses and do not raise email delivery errors. # Ignore bad email addresses and do not raise email delivery errors.
@ -65,22 +86,17 @@ Rails.application.configure do
# the I18n.default_locale when a translation cannot be found). # the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true config.i18n.fallbacks = true
# Send deprecation notices to registered listeners. # Don't log any deprecations.
config.active_support.deprecation = :notify config.active_support.report_deprecations = false
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Use a different logger for distributed setups.
# require 'syslog/logger'
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
if ENV["RAILS_LOG_TO_STDOUT"].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Do not dump schema after migrations. # Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false config.active_record.dump_schema_after_migration = false
# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end end

View File

@ -1,32 +1,41 @@
Rails.application.configure do require "active_support/core_ext/integer/time"
# Settings specified here will take precedence over those in config/application.rb.
# The test environment is used exclusively to run your application's # The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that # test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped # your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there! # and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Do not eager load code on boot. This avoids loading your whole application Rails.application.configure do
# just for the purpose of running a single test. If you are using a tool that # Settings specified here will take precedence over those in config/application.rb.
# preloads Rails for running tests, you may have to set it to true.
config.eager_load = false # While tests run files are not watched, reloading is not necessary.
config.enable_reloading = false
# Eager loading loads your entire application. When running a single test locally,
# this is usually not necessary, and can slow down your test suite. However, it's
# recommended that you enable it in continuous integration systems to ensure eager
# loading is working properly before deploying your code.
config.eager_load = ENV["CI"].present?
# Configure public file server for tests with Cache-Control for performance. # Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" }
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=3600'
}
# Show full error reports and disable caching. # Show full error reports and disable caching.
config.consider_all_requests_local = true config.consider_all_requests_local = true
config.action_controller.perform_caching = false config.action_controller.perform_caching = false
config.cache_store = :null_store
# Raise exceptions instead of rendering exception templates. # Render exception templates for rescuable exceptions and raise for other exceptions.
config.action_dispatch.show_exceptions = false config.action_dispatch.show_exceptions = :rescuable
# Disable request forgery protection in test environment. # Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
# Tell Action Mailer not to deliver emails to the real world. # Tell Action Mailer not to deliver emails to the real world.
@ -34,9 +43,25 @@ Rails.application.configure do
# ActionMailer::Base.deliveries array. # ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test config.action_mailer.delivery_method = :test
# Unlike controllers, the mailer instance doesn't have any context about the
# incoming request so you'll need to provide the :host parameter yourself.
config.action_mailer.default_url_options = { host: "www.example.com" }
# Print deprecation notices to the stderr. # Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr config.active_support.deprecation = :stderr
# Raises error for missing translations # Raise exceptions for disallowed deprecations.
# config.action_view.raise_on_missing_translations = true config.active_support.disallowed_deprecation = :raise
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
end end

View File

@ -1,6 +1,8 @@
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
# ActiveSupport::Reloader.to_prepare do
# ApplicationController.renderer.defaults.merge!( # ApplicationController.renderer.defaults.merge!(
# http_host: 'example.org', # http_host: 'example.org',
# https: false # https: false
# ) # )
# end

View File

@ -1,11 +1,12 @@
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets. # Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0' Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path # Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path # Rails.application.config.assets.paths << Emoji.images_path
# Precompile additional assets. # Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. # application.js, application.css, and all non-JS/CSS in the app/assets
# Rails.application.config.assets.precompile += %w( search.js ) # folder are already added.
# Rails.application.config.assets.precompile += %w[ admin.js admin.css ]

View File

@ -0,0 +1,25 @@
# Be sure to restart your server when you modify this file.
# Define an application-wide content security policy.
# See the Securing Rails Applications Guide for more information:
# https://guides.rubyonrails.org/security.html#content-security-policy-header
# Rails.application.configure do
# config.content_security_policy do |policy|
# policy.default_src :self, :https
# policy.font_src :self, :https, :data
# policy.img_src :self, :https, :data
# policy.object_src :none
# policy.script_src :self, :https
# policy.style_src :self, :https
# # Specify URI for violation reports
# # policy.report_uri "/csp-violation-report-endpoint"
# end
#
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end

View File

@ -1,4 +1,8 @@
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file. # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
Rails.application.config.filter_parameters += [:password] # Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [
:passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
]

View File

@ -4,13 +4,13 @@
# are locale specific, and you may define rules for as many different # are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default: # locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect| # ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1en' # inflect.plural /^(ox)$/i, "\\1en"
# inflect.singular /^(ox)en/i, '\1' # inflect.singular /^(ox)en/i, "\\1"
# inflect.irregular 'person', 'people' # inflect.irregular "person", "people"
# inflect.uncountable %w( fish sheep ) # inflect.uncountable %w( fish sheep )
# end # end
# These inflection rules are supported but not enabled by default: # These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect| # ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful' # inflect.acronym "RESTful"
# end # end

View File

@ -0,0 +1,13 @@
# Be sure to restart your server when you modify this file.
# Define an application-wide HTTP permissions policy. For further
# information see: https://developers.google.com/web/updates/2018/06/feature-policy
# Rails.application.config.permissions_policy do |policy|
# policy.camera :none
# policy.gyroscope :none
# policy.microphone :none
# policy.usb :none
# policy.fullscreen :self
# policy.payment :self, "https://secure.example.com"
# end

View File

@ -0,0 +1,5 @@
require 'net/http'
File.read(Rails.root.join('config', 'rack_attack_blocklist.txt')).split("\n").each do |blocked_ip|
Rack::Attack.blocklist_ip(blocked_ip)
end

View File

@ -1,9 +1,11 @@
bg: bg:
generic: generic:
title: Възможни лекции за OpenFest 2016 title: Възможни лекции за OpenFest 2024
submit: Изпрати submit: Изпрати
store_and_submit_changes: Запази локално промените и ги изпрати, ако е възможно store_and_submit_changes: Запази локално промените и ги изпрати, ако е възможно
help_us_reduce_the_conflicts: Помогнете ни да намалим конфликтите в програмата на OpenFest help_us_reduce_the_conflicts: Помогнете ни да намалим конфликтите в програмата на OpenFest
click_on_the_talks_you_would_like_to_watch: Кликнете (или натиснете два пъти, ако сте на мобилно устройство) върху лекциите, които искате да посетите. click_on_the_talks_you_would_like_to_watch: Кликнете (или натиснете два пъти, ако сте на мобилно устройство) върху лекциите, които искате да посетите.
press_submit: Натиснете „Изпрати“. press_submit: Натиснете „Изпрати“.
filter_events: Търсене filter_events: Търсене
status_success: Предпочитанията Ви са запазени
status_failed: Не успахме да запазим предпочитанията Ви

View File

@ -1,9 +1,11 @@
en: en:
generic: generic:
title: OpenFest 2016 Talk Preference Poll title: OpenFest 2024 Talk Preference Poll
submit: Submit submit: Submit
store_and_submit_changes: Store your changes locally and submit them if possible store_and_submit_changes: Store your changes locally and submit them if possible
help_us_reduce_the_conflicts: "Help us to reduce the conflicts in OpenFest's schedule" help_us_reduce_the_conflicts: "Help us to reduce the conflicts in OpenFest's schedule"
click_on_the_talks_you_would_like_to_watch: Click (or tap twice if on mobile device) on the talks you likely would like to watch. click_on_the_talks_you_would_like_to_watch: Click (or tap twice if on mobile device) on the talks you likely would like to watch.
press_submit: Press submit. press_submit: Press submit.
filter_events: Filter Events filter_events: Filter Events
status_success: Your preferences have been submitted
status_failed: Your preferences could not be submitted

View File

@ -1,47 +1,34 @@
# Puma can serve each request in a thread from an internal thread pool. # This configuration file will be evaluated by Puma. The top-level methods that
# The `threads` method setting takes two numbers a minimum and maximum. # are invoked here are part of Puma's configuration DSL. For more information
# Any libraries that use thread pools should be configured to match # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum, this matches the default thread size of Active Record. # Puma starts a configurable number of processes (workers) and each process
# serves each request in a thread from an internal thread pool.
# #
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i # The ideal number of threads per worker depends both on how much time the
# application spends waiting for IO operations and on how much you wish to
# to prioritize throughput over latency.
#
# As a rule of thumb, increasing the number of threads will increase how much
# traffic a given process can handle (throughput), but due to CRuby's
# Global VM Lock (GVL) it has diminishing returns and will degrade the
# response time (latency) of the application.
#
# The default is set to 3 threads as it's deemed a decent compromise between
# throughput and latency for the average Rails application.
#
# Any libraries that use a connection pool or another resource pool should
# be configured to provide at least as many connections as the number of
# threads. This includes Active Record's `pool` parameter in `database.yml`.
threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
threads threads_count, threads_count threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests, default is 3000. # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
# port ENV.fetch("PORT", 3000)
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in. # Allow puma to be restarted by `bin/rails restart` command.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory. If you use this option
# you need to make sure to reconnect any threads in the `on_worker_boot`
# block.
#
# preload_app!
# The code in the `on_worker_boot` will be called if you are using
# clustered mode by specifying a number of `workers`. After each worker
# process is booted this block will be run, if you are using `preload_app!`
# option you will want to use this block to reconnect to any threads
# or connections that may have been created at application boot, Ruby
# cannot share connections between processes.
#
# on_worker_boot do
# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
# end
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart plugin :tmp_restart
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
# In other environments, only set the PID file if requested.
pidfile ENV["PIDFILE"] if ENV["PIDFILE"]

File diff suppressed because it is too large Load Diff

View File

@ -2,5 +2,7 @@ Rails.application.routes.draw do
resources :talk_preferences, only: [:index, :show, :create, :update] resources :talk_preferences, only: [:index, :show, :create, :update]
root to: 'home#index' root to: 'home#index'
resource :summary, only: :show resource :summary, only: :show
resource :conflicts, only: :show
resource :conflicts_summary, only: :show
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end end

34
config/storage.yml Normal file
View File

@ -0,0 +1,34 @@
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
# storage_account_name: your_account_name
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
# mirror:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]

View File

@ -1,4 +1,4 @@
class CreateTalkPreferences < ActiveRecord::Migration class CreateTalkPreferences < ActiveRecord::Migration[4.2]
def change def change
create_table :talk_preferences, id: false do |t| create_table :talk_preferences, id: false do |t|
t.string :unique_id, null: false t.string :unique_id, null: false

View File

@ -1,4 +1,4 @@
class AddHashedUniqueIdToTalkPreferences < ActiveRecord::Migration class AddHashedUniqueIdToTalkPreferences < ActiveRecord::Migration[4.2]
def change def change
add_column :talk_preferences, :hashed_unique_id, :string add_column :talk_preferences, :hashed_unique_id, :string
add_index :talk_preferences, :hashed_unique_id add_index :talk_preferences, :hashed_unique_id

View File

@ -2,7 +2,7 @@ class TalkPreference < ActiveRecord::Base
self.primary_key = :unique_id self.primary_key = :unique_id
end end
class PopulateHashedUniqueIdInTalkPreferences < ActiveRecord::Migration class PopulateHashedUniqueIdInTalkPreferences < ActiveRecord::Migration[4.2]
def up def up
TalkPreference.all.each do |talk_preference| TalkPreference.all.each do |talk_preference|
talk_preference.hashed_unique_id = Digest::SHA1.hexdigest(talk_preference.unique_id) talk_preference.hashed_unique_id = Digest::SHA1.hexdigest(talk_preference.unique_id)

View File

@ -1,6 +1,6 @@
class TalkPreference < ActiveRecord::Base class TalkPreference < ActiveRecord::Base
self.primary_key = :unique_id self.primary_key = :unique_id
serialize :talks, Array serialize :talks, type: Array, coder: JSON
end end
class SelectedTalk < ApplicationRecord class SelectedTalk < ApplicationRecord

View File

@ -1,7 +1,7 @@
class TalkPreference < ActiveRecord::Base class TalkPreference < ActiveRecord::Base
self.primary_key = :unique_id self.primary_key = :unique_id
has_many :selected_talks has_many :selected_talks
serialize :talks, Array serialize :talks, type: Array, coder: JSON
end end
class SelectedTalk < ApplicationRecord class SelectedTalk < ApplicationRecord

View File

@ -0,0 +1,19 @@
CONFLICTS_FOR_TALK_VIEW_SQL = <<EOS
CREATE VIEW "conflicts_for_talks" AS
SELECT DISTINCT("left"."talk_id") AS "talk_id",
COUNT("right"."talk_preference_id") - 1 AS "conflicts"
FROM "selected_talks" AS "left"
INNER JOIN "selected_talks" AS "right"
ON "left"."talk_id" = "right"."talk_id"
GROUP BY "left"."id";
EOS
class AddConflictsForTalkView < ActiveRecord::Migration[5.0]
def up
execute CONFLICTS_FOR_TALK_VIEW_SQL
end
def down
execute 'DROP VIEW "conflicts_for_talks"'
end
end

View File

@ -0,0 +1,19 @@
CONFLICTS_VIEW_SQL = <<EOS
CREATE VIEW "conflicts" AS
SELECT "left"."talk_id" AS left, "right"."talk_id" AS right, COUNT(*) AS "conflicts"
FROM "selected_talks" AS "left"
INNER JOIN "selected_talks" AS "right"
ON "left"."talk_preference_id" = "right"."talk_preference_id"
WHERE "left"."talk_id" != "right"."talk_id"
GROUP BY "left"."talk_id", "right"."talk_id";
EOS
class AddConflictsView < ActiveRecord::Migration[5.0]
def up
execute CONFLICTS_VIEW_SQL
end
def down
execute 'DROP VIEW "conflicts"'
end
end

View File

@ -0,0 +1,10 @@
# This migration comes from active_storage (originally 20180723000244)
class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0]
def up
return if foreign_key_exists?(:active_storage_attachments, column: :blob_id)
if table_exists?(:active_storage_blobs)
add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id
end
end
end

View File

@ -0,0 +1,22 @@
# This migration comes from active_storage (originally 20190112182829)
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
def up
return unless table_exists?(:active_storage_blobs)
unless column_exists?(:active_storage_blobs, :service_name)
add_column :active_storage_blobs, :service_name, :string
if configured_service = ActiveStorage::Blob.service.name
ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
end
change_column :active_storage_blobs, :service_name, :string, null: false
end
end
def down
return unless table_exists?(:active_storage_blobs)
remove_column :active_storage_blobs, :service_name
end
end

View File

@ -0,0 +1,27 @@
# This migration comes from active_storage (originally 20191206030411)
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
# Use Active Record's configured type for primary key
create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
t.string :variation_digest, null: false
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
private
def primary_key_type
config = Rails.configuration.generators
config.options[config.orm][:primary_key_type] || :primary_key
end
def blobs_primary_key_type
pkey_name = connection.primary_key(:active_storage_blobs)
pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
pkey_column.bigint? ? :bigint : pkey_column.type
end
end

View File

@ -0,0 +1,8 @@
# This migration comes from active_storage (originally 20211119233751)
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
change_column_null(:active_storage_blobs, :checksum, true)
end
end

View File

@ -4,7 +4,7 @@
<title>The page you were looking for doesn't exist (404)</title> <title>The page you were looking for doesn't exist (404)</title>
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<style> <style>
body { .rails-default-error-page {
background-color: #EFEFEF; background-color: #EFEFEF;
color: #2E2F30; color: #2E2F30;
text-align: center; text-align: center;
@ -12,13 +12,13 @@
margin: 0; margin: 0;
} }
div.dialog { .rails-default-error-page div.dialog {
width: 95%; width: 95%;
max-width: 33em; max-width: 33em;
margin: 4em auto 0; margin: 4em auto 0;
} }
div.dialog > div { .rails-default-error-page div.dialog > div {
border: 1px solid #CCC; border: 1px solid #CCC;
border-right-color: #999; border-right-color: #999;
border-left-color: #999; border-left-color: #999;
@ -31,13 +31,13 @@
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
} }
h1 { .rails-default-error-page h1 {
font-size: 100%; font-size: 100%;
color: #730E15; color: #730E15;
line-height: 1.5em; line-height: 1.5em;
} }
div.dialog > p { .rails-default-error-page div.dialog > p {
margin: 0 0 1em; margin: 0 0 1em;
padding: 1em; padding: 1em;
background-color: #F7F7F7; background-color: #F7F7F7;
@ -54,7 +54,7 @@
</style> </style>
</head> </head>
<body> <body class="rails-default-error-page">
<!-- This file lives in public/404.html --> <!-- This file lives in public/404.html -->
<div class="dialog"> <div class="dialog">
<div> <div>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title>Your browser is not supported (406)</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
.rails-default-error-page {
background-color: #EFEFEF;
color: #2E2F30;
text-align: center;
font-family: arial, sans-serif;
margin: 0;
}
.rails-default-error-page div.dialog {
width: 95%;
max-width: 33em;
margin: 4em auto 0;
}
.rails-default-error-page div.dialog > div {
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #BBB;
border-top: #B00100 solid 4px;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
background-color: white;
padding: 7px 12% 0;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
.rails-default-error-page h1 {
font-size: 100%;
color: #730E15;
line-height: 1.5em;
}
.rails-default-error-page div.dialog > p {
margin: 0 0 1em;
padding: 1em;
background-color: #F7F7F7;
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #999;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-top-color: #DADADA;
color: #666;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
</style>
</head>
<body class="rails-default-error-page">
<!-- This file lives in public/406-unsupported-browser.html -->
<div class="dialog">
<div>
<h1>Your browser is not supported.</h1>
<p>Please upgrade your browser to continue.</p>
</div>
</div>
</body>
</html>

View File

@ -4,7 +4,7 @@
<title>The change you wanted was rejected (422)</title> <title>The change you wanted was rejected (422)</title>
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<style> <style>
body { .rails-default-error-page {
background-color: #EFEFEF; background-color: #EFEFEF;
color: #2E2F30; color: #2E2F30;
text-align: center; text-align: center;
@ -12,13 +12,13 @@
margin: 0; margin: 0;
} }
div.dialog { .rails-default-error-page div.dialog {
width: 95%; width: 95%;
max-width: 33em; max-width: 33em;
margin: 4em auto 0; margin: 4em auto 0;
} }
div.dialog > div { .rails-default-error-page div.dialog > div {
border: 1px solid #CCC; border: 1px solid #CCC;
border-right-color: #999; border-right-color: #999;
border-left-color: #999; border-left-color: #999;
@ -31,13 +31,13 @@
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
} }
h1 { .rails-default-error-page h1 {
font-size: 100%; font-size: 100%;
color: #730E15; color: #730E15;
line-height: 1.5em; line-height: 1.5em;
} }
div.dialog > p { .rails-default-error-page div.dialog > p {
margin: 0 0 1em; margin: 0 0 1em;
padding: 1em; padding: 1em;
background-color: #F7F7F7; background-color: #F7F7F7;
@ -54,7 +54,7 @@
</style> </style>
</head> </head>
<body> <body class="rails-default-error-page">
<!-- This file lives in public/422.html --> <!-- This file lives in public/422.html -->
<div class="dialog"> <div class="dialog">
<div> <div>

View File

@ -4,7 +4,7 @@
<title>We're sorry, but something went wrong (500)</title> <title>We're sorry, but something went wrong (500)</title>
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<style> <style>
body { .rails-default-error-page {
background-color: #EFEFEF; background-color: #EFEFEF;
color: #2E2F30; color: #2E2F30;
text-align: center; text-align: center;
@ -12,13 +12,13 @@
margin: 0; margin: 0;
} }
div.dialog { .rails-default-error-page div.dialog {
width: 95%; width: 95%;
max-width: 33em; max-width: 33em;
margin: 4em auto 0; margin: 4em auto 0;
} }
div.dialog > div { .rails-default-error-page div.dialog > div {
border: 1px solid #CCC; border: 1px solid #CCC;
border-right-color: #999; border-right-color: #999;
border-left-color: #999; border-left-color: #999;
@ -31,13 +31,13 @@
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
} }
h1 { .rails-default-error-page h1 {
font-size: 100%; font-size: 100%;
color: #730E15; color: #730E15;
line-height: 1.5em; line-height: 1.5em;
} }
div.dialog > p { .rails-default-error-page div.dialog > p {
margin: 0 0 1em; margin: 0 0 1em;
padding: 1em; padding: 1em;
background-color: #F7F7F7; background-color: #F7F7F7;
@ -54,7 +54,7 @@
</style> </style>
</head> </head>
<body> <body class="rails-default-error-page">
<!-- This file lives in public/500.html --> <!-- This file lives in public/500.html -->
<div class="dialog"> <div class="dialog">
<div> <div>

View File

@ -1,5 +1 @@
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-agent: *
# Disallow: /

0
storage/.keep Normal file
View File

View File

@ -0,0 +1,865 @@
/*
* jQuery Mobile v1.4.5
* http://jquerymobile.com
*
* Copyright 2010, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
*/
(function ( root, doc, factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "jquery" ], function ( $ ) {
factory( $, root, doc );
return $.mobile;
});
} else {
// Browser globals
factory( root.jQuery, root, doc );
}
}( this, document, function ( jQuery, window, document, undefined ) {
// This plugin is an experiment for abstracting away the touch and mouse
// events so that developers don't have to worry about which method of input
// the device their document is loaded on supports.
//
// The idea here is to allow the developer to register listeners for the
// basic mouse events, such as mousedown, mousemove, mouseup, and click,
// and the plugin will take care of registering the correct listeners
// behind the scenes to invoke the listener at the fastest possible time
// for that device, while still retaining the order of event firing in
// the traditional mouse environment, should multiple handlers be registered
// on the same element for different events.
//
// The current version exposes the following virtual events to jQuery bind methods:
// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
(function( $, window, document, undefined ) {
var dataPropertyName = "virtualMouseBindings",
touchTargetPropertyName = "virtualTouchID",
virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
mouseEventProps = $.event.props.concat( mouseHookProps ),
activeDocHandlers = {},
resetTimerID = 0,
startX = 0,
startY = 0,
didScroll = false,
clickBlockList = [],
blockMouseTriggers = false,
blockTouchTriggers = false,
eventCaptureSupported = "addEventListener" in document,
$document = $( document ),
nextTouchID = 1,
lastTouchID = 0, threshold,
i;
$.vmouse = {
moveDistanceThreshold: 10,
clickDistanceThreshold: 10,
resetTimerDuration: 1500
};
function getNativeEvent( event ) {
while ( event && typeof event.originalEvent !== "undefined" ) {
event = event.originalEvent;
}
return event;
}
function createVirtualEvent( event, eventType ) {
var t = event.type,
oe, props, ne, prop, ct, touch, i, j, len;
event = $.Event( event );
event.type = eventType;
oe = event.originalEvent;
props = $.event.props;
// addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
// https://github.com/jquery/jquery-mobile/issues/3280
if ( t.search( /^(mouse|click)/ ) > -1 ) {
props = mouseEventProps;
}
// copy original event properties over to the new event
// this would happen if we could call $.event.fix instead of $.Event
// but we don't have a way to force an event to be fixed multiple times
if ( oe ) {
for ( i = props.length, prop; i; ) {
prop = props[ --i ];
event[ prop ] = oe[ prop ];
}
}
// make sure that if the mouse and click virtual events are generated
// without a .which one is defined
if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ) {
event.which = 1;
}
if ( t.search(/^touch/) !== -1 ) {
ne = getNativeEvent( oe );
t = ne.touches;
ct = ne.changedTouches;
touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined );
if ( touch ) {
for ( j = 0, len = touchEventProps.length; j < len; j++) {
prop = touchEventProps[ j ];
event[ prop ] = touch[ prop ];
}
}
}
return event;
}
function getVirtualBindingFlags( element ) {
var flags = {},
b, k;
while ( element ) {
b = $.data( element, dataPropertyName );
for ( k in b ) {
if ( b[ k ] ) {
flags[ k ] = flags.hasVirtualBinding = true;
}
}
element = element.parentNode;
}
return flags;
}
function getClosestElementWithVirtualBinding( element, eventType ) {
var b;
while ( element ) {
b = $.data( element, dataPropertyName );
if ( b && ( !eventType || b[ eventType ] ) ) {
return element;
}
element = element.parentNode;
}
return null;
}
function enableTouchBindings() {
blockTouchTriggers = false;
}
function disableTouchBindings() {
blockTouchTriggers = true;
}
function enableMouseBindings() {
lastTouchID = 0;
clickBlockList.length = 0;
blockMouseTriggers = false;
// When mouse bindings are enabled, our
// touch bindings are disabled.
disableTouchBindings();
}
function disableMouseBindings() {
// When mouse bindings are disabled, our
// touch bindings are enabled.
enableTouchBindings();
}
function startResetTimer() {
clearResetTimer();
resetTimerID = setTimeout( function() {
resetTimerID = 0;
enableMouseBindings();
}, $.vmouse.resetTimerDuration );
}
function clearResetTimer() {
if ( resetTimerID ) {
clearTimeout( resetTimerID );
resetTimerID = 0;
}
}
function triggerVirtualEvent( eventType, event, flags ) {
var ve;
if ( ( flags && flags[ eventType ] ) ||
( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
ve = createVirtualEvent( event, eventType );
$( event.target).trigger( ve );
}
return ve;
}
function mouseEventCallback( event ) {
var touchID = $.data( event.target, touchTargetPropertyName ),
ve;
if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) {
ve = triggerVirtualEvent( "v" + event.type, event );
if ( ve ) {
if ( ve.isDefaultPrevented() ) {
event.preventDefault();
}
if ( ve.isPropagationStopped() ) {
event.stopPropagation();
}
if ( ve.isImmediatePropagationStopped() ) {
event.stopImmediatePropagation();
}
}
}
}
function handleTouchStart( event ) {
var touches = getNativeEvent( event ).touches,
target, flags, t;
if ( touches && touches.length === 1 ) {
target = event.target;
flags = getVirtualBindingFlags( target );
if ( flags.hasVirtualBinding ) {
lastTouchID = nextTouchID++;
$.data( target, touchTargetPropertyName, lastTouchID );
clearResetTimer();
disableMouseBindings();
didScroll = false;
t = getNativeEvent( event ).touches[ 0 ];
startX = t.pageX;
startY = t.pageY;
triggerVirtualEvent( "vmouseover", event, flags );
triggerVirtualEvent( "vmousedown", event, flags );
}
}
}
function handleScroll( event ) {
if ( blockTouchTriggers ) {
return;
}
if ( !didScroll ) {
triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
}
didScroll = true;
startResetTimer();
}
function handleTouchMove( event ) {
if ( blockTouchTriggers ) {
return;
}
var t = getNativeEvent( event ).touches[ 0 ],
didCancel = didScroll,
moveThreshold = $.vmouse.moveDistanceThreshold,
flags = getVirtualBindingFlags( event.target );
didScroll = didScroll ||
( Math.abs( t.pageX - startX ) > moveThreshold ||
Math.abs( t.pageY - startY ) > moveThreshold );
if ( didScroll && !didCancel ) {
triggerVirtualEvent( "vmousecancel", event, flags );
}
triggerVirtualEvent( "vmousemove", event, flags );
startResetTimer();
}
function handleTouchEnd( event ) {
if ( blockTouchTriggers ) {
return;
}
disableTouchBindings();
var flags = getVirtualBindingFlags( event.target ),
ve, t;
triggerVirtualEvent( "vmouseup", event, flags );
if ( !didScroll ) {
ve = triggerVirtualEvent( "vclick", event, flags );
if ( ve && ve.isDefaultPrevented() ) {
// The target of the mouse events that follow the touchend
// event don't necessarily match the target used during the
// touch. This means we need to rely on coordinates for blocking
// any click that is generated.
t = getNativeEvent( event ).changedTouches[ 0 ];
clickBlockList.push({
touchID: lastTouchID,
x: t.clientX,
y: t.clientY
});
// Prevent any mouse events that follow from triggering
// virtual event notifications.
blockMouseTriggers = true;
}
}
triggerVirtualEvent( "vmouseout", event, flags);
didScroll = false;
startResetTimer();
}
function hasVirtualBindings( ele ) {
var bindings = $.data( ele, dataPropertyName ),
k;
if ( bindings ) {
for ( k in bindings ) {
if ( bindings[ k ] ) {
return true;
}
}
}
return false;
}
function dummyMouseHandler() {}
function getSpecialEventObject( eventType ) {
var realType = eventType.substr( 1 );
return {
setup: function(/* data, namespace */) {
// If this is the first virtual mouse binding for this element,
// add a bindings object to its data.
if ( !hasVirtualBindings( this ) ) {
$.data( this, dataPropertyName, {} );
}
// If setup is called, we know it is the first binding for this
// eventType, so initialize the count for the eventType to zero.
var bindings = $.data( this, dataPropertyName );
bindings[ eventType ] = true;
// If this is the first virtual mouse event for this type,
// register a global handler on the document.
activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
if ( activeDocHandlers[ eventType ] === 1 ) {
$document.bind( realType, mouseEventCallback );
}
// Some browsers, like Opera Mini, won't dispatch mouse/click events
// for elements unless they actually have handlers registered on them.
// To get around this, we register dummy handlers on the elements.
$( this ).bind( realType, dummyMouseHandler );
// For now, if event capture is not supported, we rely on mouse handlers.
if ( eventCaptureSupported ) {
// If this is the first virtual mouse binding for the document,
// register our touchstart handler on the document.
activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
if ( activeDocHandlers[ "touchstart" ] === 1 ) {
$document.bind( "touchstart", handleTouchStart )
.bind( "touchend", handleTouchEnd )
// On touch platforms, touching the screen and then dragging your finger
// causes the window content to scroll after some distance threshold is
// exceeded. On these platforms, a scroll prevents a click event from being
// dispatched, and on some platforms, even the touchend is suppressed. To
// mimic the suppression of the click event, we need to watch for a scroll
// event. Unfortunately, some platforms like iOS don't dispatch scroll
// events until *AFTER* the user lifts their finger (touchend). This means
// we need to watch both scroll and touchmove events to figure out whether
// or not a scroll happenens before the touchend event is fired.
.bind( "touchmove", handleTouchMove )
.bind( "scroll", handleScroll );
}
}
},
teardown: function(/* data, namespace */) {
// If this is the last virtual binding for this eventType,
// remove its global handler from the document.
--activeDocHandlers[ eventType ];
if ( !activeDocHandlers[ eventType ] ) {
$document.unbind( realType, mouseEventCallback );
}
if ( eventCaptureSupported ) {
// If this is the last virtual mouse binding in existence,
// remove our document touchstart listener.
--activeDocHandlers[ "touchstart" ];
if ( !activeDocHandlers[ "touchstart" ] ) {
$document.unbind( "touchstart", handleTouchStart )
.unbind( "touchmove", handleTouchMove )
.unbind( "touchend", handleTouchEnd )
.unbind( "scroll", handleScroll );
}
}
var $this = $( this ),
bindings = $.data( this, dataPropertyName );
// teardown may be called when an element was
// removed from the DOM. If this is the case,
// jQuery core may have already stripped the element
// of any data bindings so we need to check it before
// using it.
if ( bindings ) {
bindings[ eventType ] = false;
}
// Unregister the dummy event handler.
$this.unbind( realType, dummyMouseHandler );
// If this is the last virtual mouse binding on the
// element, remove the binding data from the element.
if ( !hasVirtualBindings( this ) ) {
$this.removeData( dataPropertyName );
}
}
};
}
// Expose our custom events to the jQuery bind/unbind mechanism.
for ( i = 0; i < virtualEventNames.length; i++ ) {
$.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
}
// Add a capture click handler to block clicks.
// Note that we require event capture support for this so if the device
// doesn't support it, we punt for now and rely solely on mouse events.
if ( eventCaptureSupported ) {
document.addEventListener( "click", function( e ) {
var cnt = clickBlockList.length,
target = e.target,
x, y, ele, i, o, touchID;
if ( cnt ) {
x = e.clientX;
y = e.clientY;
threshold = $.vmouse.clickDistanceThreshold;
// The idea here is to run through the clickBlockList to see if
// the current click event is in the proximity of one of our
// vclick events that had preventDefault() called on it. If we find
// one, then we block the click.
//
// Why do we have to rely on proximity?
//
// Because the target of the touch event that triggered the vclick
// can be different from the target of the click event synthesized
// by the browser. The target of a mouse/click event that is synthesized
// from a touch event seems to be implementation specific. For example,
// some browsers will fire mouse/click events for a link that is near
// a touch event, even though the target of the touchstart/touchend event
// says the user touched outside the link. Also, it seems that with most
// browsers, the target of the mouse/click event is not calculated until the
// time it is dispatched, so if you replace an element that you touched
// with another element, the target of the mouse/click will be the new
// element underneath that point.
//
// Aside from proximity, we also check to see if the target and any
// of its ancestors were the ones that blocked a click. This is necessary
// because of the strange mouse/click target calculation done in the
// Android 2.1 browser, where if you click on an element, and there is a
// mouse/click handler on one of its ancestors, the target will be the
// innermost child of the touched element, even if that child is no where
// near the point of touch.
ele = target;
while ( ele ) {
for ( i = 0; i < cnt; i++ ) {
o = clickBlockList[ i ];
touchID = 0;
if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
$.data( ele, touchTargetPropertyName ) === o.touchID ) {
// XXX: We may want to consider removing matches from the block list
// instead of waiting for the reset timer to fire.
e.preventDefault();
e.stopPropagation();
return;
}
}
ele = ele.parentNode;
}
}
}, true);
}
})( jQuery, window, document );
(function( $ ) {
$.mobile = {};
}( jQuery ));
(function( $, undefined ) {
var support = {
touch: "ontouchend" in document
};
$.mobile.support = $.mobile.support || {};
$.extend( $.support, support );
$.extend( $.mobile.support, support );
}( jQuery ));
(function( $, window, undefined ) {
var $document = $( document ),
supportTouch = $.mobile.support.touch,
scrollEvent = "touchmove scroll",
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
touchStopEvent = supportTouch ? "touchend" : "mouseup",
touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
// setup new event shortcuts
$.each( ( "touchstart touchmove touchend " +
"tap taphold " +
"swipe swipeleft swiperight " +
"scrollstart scrollstop" ).split( " " ), function( i, name ) {
$.fn[ name ] = function( fn ) {
return fn ? this.bind( name, fn ) : this.trigger( name );
};
// jQuery < 1.8
if ( $.attrFn ) {
$.attrFn[ name ] = true;
}
});
function triggerCustomEvent( obj, eventType, event, bubble ) {
var originalType = event.type;
event.type = eventType;
if ( bubble ) {
$.event.trigger( event, undefined, obj );
} else {
$.event.dispatch.call( obj, event );
}
event.type = originalType;
}
// also handles scrollstop
$.event.special.scrollstart = {
enabled: true,
setup: function() {
var thisObject = this,
$this = $( thisObject ),
scrolling,
timer;
function trigger( event, state ) {
scrolling = state;
triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
}
// iPhone triggers scroll after a small delay; use touchmove instead
$this.bind( scrollEvent, function( event ) {
if ( !$.event.special.scrollstart.enabled ) {
return;
}
if ( !scrolling ) {
trigger( event, true );
}
clearTimeout( timer );
timer = setTimeout( function() {
trigger( event, false );
}, 50 );
});
},
teardown: function() {
$( this ).unbind( scrollEvent );
}
};
// also handles taphold
$.event.special.tap = {
tapholdThreshold: 750,
emitTapOnTaphold: true,
setup: function() {
var thisObject = this,
$this = $( thisObject ),
isTaphold = false;
$this.bind( "vmousedown", function( event ) {
isTaphold = false;
if ( event.which && event.which !== 1 ) {
return false;
}
var origTarget = event.target,
timer;
function clearTapTimer() {
clearTimeout( timer );
}
function clearTapHandlers() {
clearTapTimer();
$this.unbind( "vclick", clickHandler )
.unbind( "vmouseup", clearTapTimer );
$document.unbind( "vmousecancel", clearTapHandlers );
}
function clickHandler( event ) {
clearTapHandlers();
// ONLY trigger a 'tap' event if the start target is
// the same as the stop target.
if ( !isTaphold && origTarget === event.target ) {
triggerCustomEvent( thisObject, "tap", event );
} else if ( isTaphold ) {
event.preventDefault();
}
}
$this.bind( "vmouseup", clearTapTimer )
.bind( "vclick", clickHandler );
$document.bind( "vmousecancel", clearTapHandlers );
timer = setTimeout( function() {
if ( !$.event.special.tap.emitTapOnTaphold ) {
isTaphold = true;
}
triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
}, $.event.special.tap.tapholdThreshold );
});
},
teardown: function() {
$( this ).unbind( "vmousedown" ).unbind( "vclick" ).unbind( "vmouseup" );
$document.unbind( "vmousecancel" );
}
};
// Also handles swipeleft, swiperight
$.event.special.swipe = {
// More than this horizontal displacement, and we will suppress scrolling.
scrollSupressionThreshold: 30,
// More time than this, and it isn't a swipe.
durationThreshold: 1000,
// Swipe horizontal displacement must be more than this.
horizontalDistanceThreshold: 30,
// Swipe vertical displacement must be less than this.
verticalDistanceThreshold: 30,
getLocation: function ( event ) {
var winPageX = window.pageXOffset,
winPageY = window.pageYOffset,
x = event.clientX,
y = event.clientY;
if ( event.pageY === 0 && Math.floor( y ) > Math.floor( event.pageY ) ||
event.pageX === 0 && Math.floor( x ) > Math.floor( event.pageX ) ) {
// iOS4 clientX/clientY have the value that should have been
// in pageX/pageY. While pageX/page/ have the value 0
x = x - winPageX;
y = y - winPageY;
} else if ( y < ( event.pageY - winPageY) || x < ( event.pageX - winPageX ) ) {
// Some Android browsers have totally bogus values for clientX/Y
// when scrolling/zooming a page. Detectable since clientX/clientY
// should never be smaller than pageX/pageY minus page scroll
x = event.pageX - winPageX;
y = event.pageY - winPageY;
}
return {
x: x,
y: y
};
},
start: function( event ) {
var data = event.originalEvent.touches ?
event.originalEvent.touches[ 0 ] : event,
location = $.event.special.swipe.getLocation( data );
return {
time: ( new Date() ).getTime(),
coords: [ location.x, location.y ],
origin: $( event.target )
};
},
stop: function( event ) {
var data = event.originalEvent.touches ?
event.originalEvent.touches[ 0 ] : event,
location = $.event.special.swipe.getLocation( data );
return {
time: ( new Date() ).getTime(),
coords: [ location.x, location.y ]
};
},
handleSwipe: function( start, stop, thisObject, origTarget ) {
if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
var direction = start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight";
triggerCustomEvent( thisObject, "swipe", $.Event( "swipe", { target: origTarget, swipestart: start, swipestop: stop }), true );
triggerCustomEvent( thisObject, direction,$.Event( direction, { target: origTarget, swipestart: start, swipestop: stop } ), true );
return true;
}
return false;
},
// This serves as a flag to ensure that at most one swipe event event is
// in work at any given time
eventInProgress: false,
setup: function() {
var events,
thisObject = this,
$this = $( thisObject ),
context = {};
// Retrieve the events data for this element and add the swipe context
events = $.data( this, "mobile-events" );
if ( !events ) {
events = { length: 0 };
$.data( this, "mobile-events", events );
}
events.length++;
events.swipe = context;
context.start = function( event ) {
// Bail if we're already working on a swipe event
if ( $.event.special.swipe.eventInProgress ) {
return;
}
$.event.special.swipe.eventInProgress = true;
var stop,
start = $.event.special.swipe.start( event ),
origTarget = event.target,
emitted = false;
context.move = function( event ) {
if ( !start || event.isDefaultPrevented() ) {
return;
}
stop = $.event.special.swipe.stop( event );
if ( !emitted ) {
emitted = $.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget );
if ( emitted ) {
// Reset the context to make way for the next swipe event
$.event.special.swipe.eventInProgress = false;
}
}
// prevent scrolling
if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
event.preventDefault();
}
};
context.stop = function() {
emitted = true;
// Reset the context to make way for the next swipe event
$.event.special.swipe.eventInProgress = false;
$document.off( touchMoveEvent, context.move );
context.move = null;
};
$document.on( touchMoveEvent, context.move )
.one( touchStopEvent, context.stop );
};
$this.on( touchStartEvent, context.start );
},
teardown: function() {
var events, context;
events = $.data( this, "mobile-events" );
if ( events ) {
context = events.swipe;
delete events.swipe;
events.length--;
if ( events.length === 0 ) {
$.removeData( this, "mobile-events" );
}
}
if ( context ) {
if ( context.start ) {
$( this ).off( touchStartEvent, context.start );
}
if ( context.move ) {
$document.off( touchMoveEvent, context.move );
}
if ( context.stop ) {
$document.off( touchStopEvent, context.stop );
}
}
}
};
$.each({
scrollstop: "scrollstart",
taphold: "tap",
swipeleft: "swipe.left",
swiperight: "swipe.right"
}, function( event, sourceEvent ) {
$.event.special[ event ] = {
setup: function() {
$( this ).bind( sourceEvent, $.noop );
},
teardown: function() {
$( this ).unbind( sourceEvent );
}
};
});
})( jQuery, this );
}));