diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..ded31c0 --- /dev/null +++ b/.babelrc @@ -0,0 +1,18 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": "> 1%", + "uglify": true + }, + "useBuiltIns": true + }] + ], + + "plugins": [ + "syntax-dynamic-import", + "transform-object-rest-spread", + ["transform-class-properties", { "spec": true }] + ] +} diff --git a/.dockerignore b/.dockerignore index f9df56c..015453b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,7 @@ .git/ -logs/*.* +log/*.* db/*.sqlite* tmp/*.* public/assets +public/packs +node_modules/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index a2479fd..be82dae 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ /public/assets .DS_Store +/public/packs +/public/packs-test +/node_modules +yarn-debug.log* +.yarn-integrity diff --git a/.postcssrc.yml b/.postcssrc.yml new file mode 100644 index 0000000..e43bed1 --- /dev/null +++ b/.postcssrc.yml @@ -0,0 +1,7 @@ +plugins: + postcss-import: {} + postcss-cssnext: { + features: { + customProperties: false + } + } \ No newline at end of file diff --git a/.ruby-version b/.ruby-version index 35cee72..ab6d278 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.3 +2.4.4 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6c4f61c..f540915 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,16 @@ FROM phusion/passenger-ruby24:latest # Use baseimage-docker's init process. CMD ["/sbin/my_init"] +# Install Node +RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \ + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends nodejs yarn && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN gem update --system && gem install bundler + # Enable Nginx / Passenger RUN rm -f /etc/service/nginx/down @@ -21,25 +31,20 @@ ENV RAILS_ENV docker ENV PASSENGER_APP_ENV docker # Setup directory and install gems -RUN mkdir -p /home/app/parsley/ -COPY Gemfile /home/app/parsley/ -COPY Gemfile.lock /home/app/parsley/ -RUN gem install bundler -RUN cd /home/app/parsley/ && bundle install --jobs 4 - -# Copy the app into the image -COPY . /home/app/parsley/ +RUN mkdir -p /home/app/parsley/log /home/app/parsley/tmp +RUN chown -R app:app /home/app/parsley/ WORKDIR /home/app/parsley/ -# Set log permissions -RUN mkdir -p /home/app/parsley/log -RUN chmod 0777 /home/app/parsley/log +COPY Gemfile* ./ +RUN bundle install --deployment --jobs 4 --without development test + +COPY package.json yarn.lock ./ +RUN yarn install --production=true + +# Copy the app into the image +COPY --chown="app" . . # Compile assets -RUN env RAILS_ENV=production bundle exec rake assets:clobber assets:precompile +RUN su app -c "bundle exec rails webpacker:clobber webpacker:compile" -# Set ownership of the tmp folder -RUN chown -R app:app /home/app/parsley/tmp -# Clean up APT when done. -RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/Gemfile b/Gemfile index 6924713..3773a2b 100644 --- a/Gemfile +++ b/Gemfile @@ -1,26 +1,22 @@ source 'https://rubygems.org' -gem 'rails', '5.0.6' -gem 'sqlite3' -gem 'pg', '~> 0.21.0' -gem 'sass-rails', '~> 5.0' -gem 'uglifier', '>= 1.3.0' +gem 'rails', '5.2.0' +gem 'pg', '~> 1.0.0' -gem 'puma' +gem 'webpacker', '3.5.3' +gem 'bootsnap', '>= 1.1.0', require: false -# See https://github.com/rails/execjs#readme for more supported runtimes -gem 'therubyracer', platforms: :ruby - -# Use jquery as the JavaScript library -gem 'jquery-rails', '~> 4.3.1' -gem 'bootstrap-sass', '~> 3.3.7' -gem 'kaminari', '~> 1.1.1' -gem 'turbolinks', '~> 5.1.0' gem 'jbuilder', '~> 2.7' -gem 'cocoon', '~> 1.2.9' +#gem 'jbuilder', git: 'https://github.com/rails/jbuilder', branch: 'master' + +gem 'oj', '~> 3.6.2' + +gem 'kaminari', '~> 1.1.1' gem 'unitwise', '~> 2.2.0' gem 'redcarpet', '~> 3.4.0' +gem 'dalli', '~> 2.7.6' + # Use ActiveModel has_secure_password gem 'bcrypt', '~> 3.1.11' @@ -28,11 +24,14 @@ gem 'tzinfo-data' group :development, :test do + gem 'puma', '~> 3.11' + gem 'sqlite3' + gem 'guard', '~> 2.14.0' gem 'guard-rspec', require: false - gem 'rspec-rails', '~> 3.5.0' + gem 'rspec-rails', '~> 3.7.2' gem 'rails-controller-testing' - gem 'factory_girl_rails', '~> 4.8.0' - gem 'database_cleaner', '~> 1.5.3' + gem 'factory_bot_rails', '~> 4.8.2' + gem 'database_cleaner', '~> 1.6.2' end diff --git a/Gemfile.lock b/Gemfile.lock index cde4cfd..5c9b1d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,65 +1,65 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.0.6) - actionpack (= 5.0.6) - nio4r (>= 1.2, < 3.0) - websocket-driver (~> 0.6.1) - actionmailer (5.0.6) - actionpack (= 5.0.6) - actionview (= 5.0.6) - activejob (= 5.0.6) + actioncable (5.2.0) + actionpack (= 5.2.0) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.0) + actionpack (= 5.2.0) + actionview (= 5.2.0) + activejob (= 5.2.0) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.6) - actionview (= 5.0.6) - activesupport (= 5.0.6) + actionpack (5.2.0) + actionview (= 5.2.0) + activesupport (= 5.2.0) rack (~> 2.0) - rack-test (~> 0.6.3) + rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.6) - activesupport (= 5.0.6) + actionview (5.2.0) + activesupport (= 5.2.0) builder (~> 3.1) - erubis (~> 2.7.0) + erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.0.6) - activesupport (= 5.0.6) + activejob (5.2.0) + activesupport (= 5.2.0) globalid (>= 0.3.6) - activemodel (5.0.6) - activesupport (= 5.0.6) - activerecord (5.0.6) - activemodel (= 5.0.6) - activesupport (= 5.0.6) - arel (~> 7.0) - activesupport (5.0.6) + activemodel (5.2.0) + activesupport (= 5.2.0) + activerecord (5.2.0) + activemodel (= 5.2.0) + activesupport (= 5.2.0) + arel (>= 9.0) + activestorage (5.2.0) + actionpack (= 5.2.0) + activerecord (= 5.2.0) + marcel (~> 0.3.1) + activesupport (5.2.0) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - arel (7.1.4) - autoprefixer-rails (8.1.0.1) - execjs - bcrypt (3.1.11) - bootstrap-sass (3.3.7) - autoprefixer-rails (>= 5.2.1) - sass (>= 3.3.4) + arel (9.0.0) + bcrypt (3.1.12) + bootsnap (1.3.0) + msgpack (~> 1.0) builder (3.2.3) - cocoon (1.2.11) coderay (1.1.2) concurrent-ruby (1.0.5) - crass (1.0.3) - database_cleaner (1.5.3) + crass (1.0.4) + dalli (2.7.8) + database_cleaner (1.6.2) diff-lcs (1.3) - erubis (2.7.0) - execjs (2.7.0) - factory_girl (4.8.1) + erubi (1.7.1) + factory_bot (4.8.2) activesupport (>= 3.0.0) - factory_girl_rails (4.8.0) - factory_girl (~> 4.8.0) + factory_bot_rails (4.8.2) + factory_bot (~> 4.8.2) railties (>= 3.0.0) - ffi (1.9.23) + ffi (1.9.25) formatador (0.2.5) globalid (0.4.1) activesupport (>= 4.2.0) @@ -77,15 +77,11 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - i18n (0.9.5) + i18n (1.0.1) concurrent-ruby (~> 1.0) jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) - jquery-rails (4.3.1) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) kaminari (1.1.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.1.1) @@ -98,52 +94,59 @@ GEM activerecord kaminari-core (= 1.1.1) kaminari-core (1.1.1) - libv8 (3.16.14.19) liner (0.2.4) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.2.1) + loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) - lumberjack (1.0.12) + lumberjack (1.0.13) mail (2.7.0) mini_mime (>= 0.1.1) + marcel (0.3.2) + mimemagic (~> 0.3.2) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) method_source (0.9.0) + mimemagic (0.3.2) mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.11.3) + msgpack (1.2.4) multi_json (1.13.1) nenv (0.3.0) - nio4r (2.3.0) + nio4r (2.3.1) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) + oj (3.6.2) parslet (1.8.2) - pg (0.21.0) + pg (1.0.0) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) - puma (3.11.3) - rack (2.0.4) - rack-test (0.6.3) - rack (>= 1.0) - rails (5.0.6) - actioncable (= 5.0.6) - actionmailer (= 5.0.6) - actionpack (= 5.0.6) - actionview (= 5.0.6) - activejob (= 5.0.6) - activemodel (= 5.0.6) - activerecord (= 5.0.6) - activesupport (= 5.0.6) + puma (3.11.4) + rack (2.0.5) + rack-proxy (0.6.4) + rack + rack-test (1.0.0) + rack (>= 1.0, < 3) + rails (5.2.0) + actioncable (= 5.2.0) + actionmailer (= 5.2.0) + actionpack (= 5.2.0) + actionview (= 5.2.0) + activejob (= 5.2.0) + activemodel (= 5.2.0) + activerecord (= 5.2.0) + activestorage (= 5.2.0) + activesupport (= 5.2.0) bundler (>= 1.3.0) - railties (= 5.0.6) + railties (= 5.2.0) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -152,53 +155,41 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.0.6) - actionpack (= 5.0.6) - activesupport (= 5.0.6) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + railties (5.2.0) + actionpack (= 5.2.0) + activesupport (= 5.2.0) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (12.3.0) + rake (12.3.1) rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) redcarpet (3.4.0) - ref (2.0.0) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) + rspec (3.7.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-core (3.7.1) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-mocks (3.5.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-rails (3.5.2) + rspec-support (~> 3.7.0) + rspec-rails (3.7.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-support (~> 3.7.0) + rspec-support (3.7.1) ruby_dep (1.5.0) - sass (3.5.5) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (5.0.7) - railties (>= 4.0.0, < 6) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) shellany (0.0.1) signed_multiset (0.2.1) sprockets (3.7.1) @@ -209,27 +200,22 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.13) - therubyracer (0.12.3) - libv8 (~> 3.16.14.15) - ref thor (0.20.0) thread_safe (0.3.6) - tilt (2.0.8) - turbolinks (5.1.0) - turbolinks-source (~> 5.1) - turbolinks-source (5.1.0) tzinfo (1.2.5) thread_safe (~> 0.1) - tzinfo-data (1.2018.3) + tzinfo-data (1.2018.5) tzinfo (>= 1.0.0) - uglifier (4.1.8) - execjs (>= 0.3.0, < 3) unitwise (2.2.0) liner (~> 0.2) memoizable (~> 0.4) parslet (~> 1.5) signed_multiset (~> 0.2) - websocket-driver (0.6.5) + webpacker (3.5.3) + activesupport (>= 4.2) + rack-proxy (>= 0.6.1) + railties (>= 4.2) + websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) @@ -238,28 +224,25 @@ PLATFORMS DEPENDENCIES bcrypt (~> 3.1.11) - bootstrap-sass (~> 3.3.7) - cocoon (~> 1.2.9) - database_cleaner (~> 1.5.3) - factory_girl_rails (~> 4.8.0) + bootsnap (>= 1.1.0) + dalli (~> 2.7.6) + database_cleaner (~> 1.6.2) + factory_bot_rails (~> 4.8.2) guard (~> 2.14.0) guard-rspec jbuilder (~> 2.7) - jquery-rails (~> 4.3.1) kaminari (~> 1.1.1) - pg (~> 0.21.0) - puma - rails (= 5.0.6) + oj (~> 3.6.2) + pg (~> 1.0.0) + puma (~> 3.11) + rails (= 5.2.0) rails-controller-testing redcarpet (~> 3.4.0) - rspec-rails (~> 3.5.0) - sass-rails (~> 5.0) + rspec-rails (~> 3.7.2) sqlite3 - therubyracer - turbolinks (~> 5.1.0) tzinfo-data - uglifier (>= 1.3.0) unitwise (~> 2.2.0) + webpacker (= 3.5.3) BUNDLED WITH 1.16.1 diff --git a/LICENSE b/LICENSE index 3d88d01..115e637 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Dan Elbert +Copyright (c) 2018 Dan Elbert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 100c8d8..a4af231 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A self hosted cookbook Parsley is released under the MIT License. -Copyright (C) 2016 Dan Elbert +Copyright (C) 2018 Dan Elbert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/assets/images/grey_wash_wall.png b/app/assets/images/grey_wash_wall.png deleted file mode 100644 index d54e889..0000000 Binary files a/app/assets/images/grey_wash_wall.png and /dev/null differ diff --git a/app/assets/images/old_map.png b/app/assets/images/old_map.png deleted file mode 100644 index eb2373b..0000000 Binary files a/app/assets/images/old_map.png and /dev/null differ diff --git a/app/assets/images/old_mathematics.png b/app/assets/images/old_mathematics.png deleted file mode 100644 index 95285ff..0000000 Binary files a/app/assets/images/old_mathematics.png and /dev/null differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 52f2c50..b741814 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,24 +10,6 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // -//= require jquery -//= require jquery_ujs -//= require turbolinks -//= require bootstrap-sprockets -//= require bootstrap-datepicker -//= require bootstrap-tagsinput -//= require cocoon -//= require typeahead -//= require autosize -//= require chosen.jquery -//= require codemirror -//= require markdown -//= require underscore + //= require_tree . -// Setup star rating automagic -$(document).on("turbolinks:load", function() { - - $("input[data-rating='true']").starRating(); - -}); \ No newline at end of file diff --git a/app/assets/javascripts/calculator.js b/app/assets/javascripts/calculator.js deleted file mode 100644 index 106b795..0000000 --- a/app/assets/javascripts/calculator.js +++ /dev/null @@ -1,124 +0,0 @@ -(function($) { - - function State() { - this.input = null; - this.outputUnit = null; - this.density = null; - this.changed = false; - } - - State.prototype.setInput = function(value) { - if (value != this.input) { - this.changed = true; - this.input = value; - } - }; - - State.prototype.setDensity = function(value) { - if (value != this.density) { - this.changed = true; - this.density = value; - } - }; - - State.prototype.setOutputUnit = function(value) { - if (value != this.outputUnit) { - this.changed = true; - this.outputUnit = value; - } - }; - - State.prototype.is_changed = function() { - return this.changed; - }; - - State.prototype.reset = function() { - this.changed = false; - }; - - var ingredientSearchEngine = new Bloodhound({ - initialize: false, - datumTokenizer: function(datum) { - return Bloodhound.tokenizers.whitespace(datum.name); - }, - queryTokenizer: Bloodhound.tokenizers.whitespace, - identify: function(datum) { return datum.id; }, - sorter: function(a, b) { - if (a.name < b.name) { - return -1; - } else if (b.name < a.name) { - return 1; - } else { - return 0; - } - }, - remote: { - url: '/calculator/ingredient_search.json?query=%QUERY', - wildcard: '%QUERY' - } - }); - - $(document).on("turbolinks:load", function() { - - var state = new State(); - var $input = $("#input"); - var $output = $("#output"); - var $density = $("#density"); - var $outputUnit = $("#output_unit"); - - var performUpdate = _.debounce(function() { - $.getJSON( - "/calculator/calculate", - {input: $input.val(), output_unit: $outputUnit.val(), density: $density.val()}, - function(data) { - if (data.errors.length) { - $("#errors_panel").show(); - $("#errors_container").html(data.errors.join(" ")); - } else { - $("#errors_panel").hide(); - } - - $output.val(data.output); - } - ); - - }, 500); - - var ingredientPicked = function(i) { - $density.val(i.density); - $density.trigger('change'); - }; - - $input.add($outputUnit).add($density).on('change blur keyup', function(evt) { - state.setInput($input.val()); - state.setOutputUnit($outputUnit.val()); - state.setDensity($density.val()); - - if (state.is_changed()) { - performUpdate(); - state.reset(); - } - }); - - if ($("#calculator").length) { - ingredientSearchEngine.initialize(false); - - $("#ingredient").typeahead({}, - { - name: 'ingredients', - source: ingredientSearchEngine, - display: function(datum) { - return datum.name; - } - }) - .on("typeahead:select", function(evt, value) { - ingredientPicked(value); - }) - .on("typeahead:autocomplete", function(evt, value) { - ingredientPicked(value); - }); - } - - }); - -})(jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/checkable.js b/app/assets/javascripts/checkable.js deleted file mode 100644 index 42f5893..0000000 --- a/app/assets/javascripts/checkable.js +++ /dev/null @@ -1,36 +0,0 @@ -(function($) { - - var pluginName = "checkable"; - - var defaultOptions = { - childrenSelector: "li", - selectedClass: "checked" - }; - - var methods = { - initialize: function (opts, sources) { - - return this.each(function() { - var options = $.extend({}, defaultOptions, opts); - $(this).on("click", options.childrenSelector, function(evt) { - $(evt.currentTarget).toggleClass(options.selectedClass); - }); - }); - } - }; - - var privateMethods = { - - }; - - $.fn[pluginName] = function (method) { - if (methods[method]) { - return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); - } else if (typeof method === 'object' || ! method) { - return methods.initialize.apply(this, arguments); - } else { - $.error('Method ' + method + ' does not exist on jQuery.' + pluginName); - } - }; - -})(jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/flash_messages.js b/app/assets/javascripts/flash_messages.js deleted file mode 100644 index a603ecf..0000000 --- a/app/assets/javascripts/flash_messages.js +++ /dev/null @@ -1,46 +0,0 @@ - -var flashMessageTypeMap = { - error: "danger", - notice: "success" -}; - -function flashMessage(flashType, message) { - - var timeoutIdContainer = {}; - - if (flashMessageTypeMap[flashType]) { - flashType = flashMessageTypeMap[flashType]; - } - - var closeButton = $(" - - - - - -
-
- -
- <%= f.label :density, class: 'control-label' %> - <%= f.text_field :density, class: 'form-control', disabled: has_ndbn %> -
- -
-
-

Ingredient Units

-
-
-
- <%= f.fields_for :ingredient_units do |iu_form| %> - <%= render partial: 'ingredients/ingredient_unit_fields', locals: {f: iu_form } %> - <% end %> - - <%= link_to_add_association 'add unit', f, :ingredient_units, class: 'btn btn-primary' %> -
-
-
- - <% if has_ndbn %> - -
-
-

NDBN Unit Weights

-
-
- - - - - - - - <% @ingredient.usda_food.usda_food_weights.each do |w| %> - - - - - <% end %> -
NameGrams
<%= "#{w.amount} #{w.description}" %><%= w.gram_weight %>
-
-
- - <% end %> - -
- <%= f.label :notes, class: 'control-label' %> - <%= f.text_area :notes, class: 'form-control' %> -
- -
-
-

Nutrition Per 100 grams

-
-
-
> - -
-
-
- <%= f.label :water, "Grams of Water", class: 'control-label' %> - <%= f.text_field :water, class: 'form-control' %> -
- -
- <%= f.label :protein, "Grams of Protein", class: 'control-label' %> - <%= f.text_field :protein, class: 'form-control' %> -
- -
- <%= f.label :lipids, "Grams of Fat", class: 'control-label' %> - <%= f.text_field :lipids, class: 'form-control' %> -
- -
- <%= f.label :carbohydrates, "Grams of Carbohydrates", class: 'control-label' %> - <%= f.text_field :carbohydrates, class: 'form-control' %> -
- -
- <%= f.label :kcal, "Calories", class: 'control-label' %> - <%= f.text_field :kcal, class: 'form-control' %> -
- -
- <%= f.label :fiber, "Grams of Fiber", class: 'control-label' %> - <%= f.text_field :fiber, class: 'form-control' %> -
- -
- <%= f.label :sugar, "Grams of Sugar", class: 'control-label' %> - <%= f.text_field :sugar, class: 'form-control' %> -
-
-
-
- <%= f.label :calcium, "Miligrams of Calcium", class: 'control-label' %> - <%= f.text_field :calcium, class: 'form-control' %> -
- -
- <%= f.label :sodium, "Miligrams of Sodium", class: 'control-label' %> - <%= f.text_field :sodium, class: 'form-control' %> -
- -
- <%= f.label :vit_k, "Micrograms of Vitamin K", class: 'control-label' %> - <%= f.text_field :vit_k, class: 'form-control' %> -
-
-
-
-
-
- -
- <%= f.submit class: 'btn btn-primary' %> -
- -<% end %> diff --git a/app/views/ingredients/_ingredient_unit_fields.html.erb b/app/views/ingredients/_ingredient_unit_fields.html.erb deleted file mode 100644 index 804bbf8..0000000 --- a/app/views/ingredients/_ingredient_unit_fields.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -
- -
- <%= f.label :name, class: 'control-label' %> - <%= f.text_field :name, class: 'form-control' %> -
- -
- <%= f.label :gram_weight, 'Grams', class: 'control-label' %> - <%= f.text_field :gram_weight, class: 'form-control' %> -
- -
- -

- <%= link_to_remove_association f, class: 'btn btn-danger btn-sm' do %> - - <% end %> -

-
- -
\ No newline at end of file diff --git a/app/views/ingredients/edit.html.erb b/app/views/ingredients/edit.html.erb deleted file mode 100644 index 9e14d8a..0000000 --- a/app/views/ingredients/edit.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -
-
- -

Editing Ingredient

- - <%= render 'form' %> - - <%= link_to 'Back', ingredients_path, class: 'btn btn-default' %> - -
-
\ No newline at end of file diff --git a/app/views/ingredients/index.html.erb b/app/views/ingredients/index.html.erb deleted file mode 100644 index 1639e4c..0000000 --- a/app/views/ingredients/index.html.erb +++ /dev/null @@ -1,58 +0,0 @@ -
-
- - - <% if @ingredients.empty? %> -

No Ingredients

- <% else %> - - <% if current_user? %> - <%= link_to 'New Ingredient', new_ingredient_path, class: 'btn btn-default' %> - <% end %> - -
- - - - - - - - - <% if current_user? %> - - <% end %> - - - - - <% decorate(@ingredients, IngredientDecorator).each do |ingredient| %> - - - - - - <% if current_user? %> - - <% end %> - - <% end %> - -
NameUSDAKCal per 100gDensity (oz/cup)
<%= link_to_if current_user?, ingredient.name, edit_ingredient_path(ingredient) %><%= ingredient.ndbn_check %><%= ingredient.kcal %><%= ingredient.density %> - <%= link_to ingredient, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %> - - <% end %> -
- <% end %> - -
- - <% if current_user? %> - <%= link_to 'New Ingredient', new_ingredient_path, class: 'btn btn-default' %> - <% end %> - - -
-
diff --git a/app/views/ingredients/index.json.jbuilder b/app/views/ingredients/index.json.jbuilder new file mode 100644 index 0000000..266b0b5 --- /dev/null +++ b/app/views/ingredients/index.json.jbuilder @@ -0,0 +1,20 @@ + +json.cache_root! [Ingredient.all, @ingredients] do + + json.extract! @ingredients, :total_count, :total_pages, :current_page + json.page_size @ingredients.limit_value + + json.ingredients @ingredients do |i| + json.extract! i, :id, :name, :ndbn, :kcal + json.usda i.ndbn.present? + + if i.density.present? + value = UnitConversion::parse(i.density) + json.density value.convert('oz/cup').change_formatter(UnitConversion::DecimalFormatter.new).pretty_value + else + json.density nil + end + + end + +end \ No newline at end of file diff --git a/app/views/ingredients/new.html.erb b/app/views/ingredients/new.html.erb deleted file mode 100644 index 2262b48..0000000 --- a/app/views/ingredients/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -
-
- -

New Ingredient

- - <%= render 'form' %> - - <%= link_to 'Back', ingredients_path, class: 'btn btn-primary' %> - -
-
diff --git a/app/views/ingredients/select_ndbn.js.erb b/app/views/ingredients/select_ndbn.js.erb deleted file mode 100644 index 4eb7e32..0000000 --- a/app/views/ingredients/select_ndbn.js.erb +++ /dev/null @@ -1,3 +0,0 @@ - -$("#ingredient_form").replaceWith($("<%= escape_javascript(render(partial: 'ingredients/form')) %>")); -window.INGREDIENT_API.initialize(); \ No newline at end of file diff --git a/app/views/ingredients/show.json.jbuilder b/app/views/ingredients/show.json.jbuilder new file mode 100644 index 0000000..6dcaf6f --- /dev/null +++ b/app/views/ingredients/show.json.jbuilder @@ -0,0 +1,46 @@ + +json.extract! @ingredient, + :id, + :name, + :ndbn, + :usda_food_name, + :notes, + :density, + :water, + :ash, + :protein, + :kcal, + :fiber, + :sugar, + :carbohydrates, + :calcium, + :iron, + :magnesium, + :phosphorus, + :potassium, + :sodium, + :zinc, + :copper, + :manganese, + :vit_c, + :vit_b6, + :vit_b12, + :vit_a, + :vit_e, + :vit_d, + :vit_k, + :cholesterol, + :lipids + +if @ingredient.ndbn.present? + json.ndbn_units @ingredient.usda_food.usda_food_weights do |fw| + json.extract! fw, :amount, :description, :gram_weight + end +else + json.ndbn_units [] +end + +json.ingredient_units @ingredient.ingredient_units do |iu| + json.extract! iu, :id, :name, :gram_weight + json._destroy false +end \ No newline at end of file diff --git a/app/views/ingredients/usda_food_search.html.erb b/app/views/ingredients/usda_food_search.html.erb deleted file mode 100644 index d8db16a..0000000 --- a/app/views/ingredients/usda_food_search.html.erb +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - -<% @foods.each do |f| %> - - - - - - - - - -<% end %> - -
per 100 grams
NDBNNameKCalCarbsSugar
<%= f.ndbn %><%= link_to f.long_description, '#', class: 'food_result', data: {ndbn: f.ndbn} %><%= f.kcal %><%= f.carbohydrates %><%= f.sugar %>
\ No newline at end of file diff --git a/app/views/ingredients/usda_food_search.json.jbuilder b/app/views/ingredients/usda_food_search.json.jbuilder index c5e240c..46d9e0f 100644 --- a/app/views/ingredients/usda_food_search.json.jbuilder +++ b/app/views/ingredients/usda_food_search.json.jbuilder @@ -1,7 +1,8 @@ json.array! @foods do |f| - json.extract! f, :ndbn + json.extract! f, :ndbn, :kcal, :carbohydrates, :lipid, :protein json.name f.long_description + end \ No newline at end of file diff --git a/app/views/kaminari/_first_page.html.erb b/app/views/kaminari/_first_page.html.erb deleted file mode 100644 index bf23ff0..0000000 --- a/app/views/kaminari/_first_page.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
  • - <%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote %> -
  • diff --git a/app/views/kaminari/_gap.html.erb b/app/views/kaminari/_gap.html.erb deleted file mode 100644 index 6d3a149..0000000 --- a/app/views/kaminari/_gap.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
  • - <%= content_tag :a, raw(t 'views.pagination.truncate') %> -
  • diff --git a/app/views/kaminari/_last_page.html.erb b/app/views/kaminari/_last_page.html.erb deleted file mode 100644 index fb619ea..0000000 --- a/app/views/kaminari/_last_page.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
  • - <%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} %> -
  • diff --git a/app/views/kaminari/_next_page.html.erb b/app/views/kaminari/_next_page.html.erb deleted file mode 100644 index 15e10e4..0000000 --- a/app/views/kaminari/_next_page.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
  • - <%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote %> -
  • diff --git a/app/views/kaminari/_page.html.erb b/app/views/kaminari/_page.html.erb deleted file mode 100644 index 8028b45..0000000 --- a/app/views/kaminari/_page.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<% if page.current? %> -
  • - <%= content_tag :a, page, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %> -
  • -<% else %> -
  • - <%= link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %> -
  • -<% end %> diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb deleted file mode 100644 index 2c8757b..0000000 --- a/app/views/kaminari/_paginator.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<%= paginator.render do -%> - -<% end -%> diff --git a/app/views/kaminari/_prev_page.html.erb b/app/views/kaminari/_prev_page.html.erb deleted file mode 100644 index d94a50a..0000000 --- a/app/views/kaminari/_prev_page.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
  • - <%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote %> -
  • diff --git a/app/views/layouts/_flash_messages.html.erb b/app/views/layouts/_flash_messages.html.erb deleted file mode 100644 index e4e8932..0000000 --- a/app/views/layouts/_flash_messages.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
    - -
    - <% flash.each do |type, values| %> - <% Array.wrap(values).each do |value| %> -
    <%= value %>
    - <% end %> - <% end %> -
    - diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index e4e688b..7ad8e82 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -9,40 +9,11 @@ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> - - <%= csrf_meta_tags %> - - -<%= render partial: 'layouts/flash_messages' %>
    @@ -50,18 +21,6 @@
    - - - - - - - - - - -<%= yield(:page_bottom) %> - \ No newline at end of file diff --git a/app/views/logs/_form.html.erb b/app/views/logs/_form.html.erb deleted file mode 100644 index fd51552..0000000 --- a/app/views/logs/_form.html.erb +++ /dev/null @@ -1,46 +0,0 @@ -<% @log = decorate(@log, LogDecorator) %> - -<%= form_for([@recipe, @log], html: {class: 'log-form'}) do |f| %> - - <%= render partial: 'shared/error_list', locals: {model: @log} %> - -
    -
    -
    - <%= f.label :date, class: 'control-label' %> - <%= f.text_field :date, class: 'form-control datepicker', value: @log.date %> -
    -
    - -
    -
    - <%= f.label :rating, class: 'control-label' %> - <%= f.text_field :rating, class: 'form-control', data: {rating: 'true'} %> -
    -
    - -
    -
    - <%= f.label :notes, 'Log Notes', class: 'control-label' %> - <%= f.text_area :notes, class: 'form-control' %> -
    -
    - -
    -
    - <%= f.submit class: 'btn btn-primary' %> -
    -
    -
    - - <%= f.fields_for(:recipe, include_id: false) do |rf| %> - <%= render partial: 'recipes/editor', locals: {f: rf} %> - <% end %> -

    - -
    - <%= f.submit class: 'btn btn-primary' %> -
    - -<% end %> - diff --git a/app/views/logs/edit.html.erb b/app/views/logs/edit.html.erb deleted file mode 100644 index 4a704e2..0000000 --- a/app/views/logs/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - - - <%= render 'form' %> - - <%= link_to 'Back', logs_path, class: 'btn btn-default' %> - -
    -
    diff --git a/app/views/logs/index.html.erb b/app/views/logs/index.html.erb deleted file mode 100644 index d0ef92a..0000000 --- a/app/views/logs/index.html.erb +++ /dev/null @@ -1,40 +0,0 @@ -
    -
    - - - - <% if @logs.empty? %> -

    No Entries

    - <% else %> - -
    - - - - - - - - - - - - <% decorate(@logs, LogDecorator).each do |log| %> - - - - - - - <% end %> - -
    RecipeDateRatingNotes
    <%= link_to log.recipe.short_name, log %><%= log.date %><%= text_field_tag('rating', log.rating, disabled: true, data: {rating: true}) %><%= log.notes %>
    -
    - - <% end %> - -
    -
    - diff --git a/app/views/logs/index.json.jbuilder b/app/views/logs/index.json.jbuilder new file mode 100644 index 0000000..90d9dfa --- /dev/null +++ b/app/views/logs/index.json.jbuilder @@ -0,0 +1,16 @@ + +json.cache_root! [Log.all, @logs] do + + json.extract! @logs, :total_count, :total_pages, :current_page + json.page_size @logs.limit_value + + json.logs @logs do |l| + json.extract! l, :id, :date, :rating, :notes + + json.recipe do + json.id l.recipe.id + json.name l.recipe.name + end + end + +end \ No newline at end of file diff --git a/app/views/logs/new.html.erb b/app/views/logs/new.html.erb deleted file mode 100644 index 0fd5857..0000000 --- a/app/views/logs/new.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - - - <%= render 'form' %> - - <%= link_to 'Back', recipes_path, class: 'btn btn-default' %> - -
    -
    diff --git a/app/views/logs/show.html.erb b/app/views/logs/show.html.erb deleted file mode 100644 index 7019342..0000000 --- a/app/views/logs/show.html.erb +++ /dev/null @@ -1,92 +0,0 @@ - -<% - @log = decorate(@log, LogDecorator) - @recipe = @log.recipe -%> - -
    - -
    -
    - - -

    Date: <%= @log.date %>

    -

    Rating: <%= text_field_tag('rating', @log.rating, disabled: true, data: {rating: true}) %>

    -

    Notes: <%= @log.notes %>

    - -

    - <%= @recipe.description %> -

    -
    -
    - -
    - <% if @recipe.total_time.present? || @recipe.active_time.present? %> -
    -

    <%= recipe_time(@recipe) %>

    -
    - <% end %> - - <% if @recipe.yields.present? %> -
    -

    Yields

    <%= @recipe.yields %>

    -
    - <% end %> - - <% if @recipe.source.present? %> -
    -

    Source

    <%= @recipe.source_markup %>

    -
    - <% end %> - -
    - -
    -
    -
    -
    - -
    -

    Ingredients

    -
    - - -
    -
    -
      - <% @recipe.recipe_ingredients.each do |i| %> -
    • <%= i.display_name %>
    • - <% end %> -
    -
    -
    - -
    - -
    -
    -
    -

    Directions

    -
    -
    - <%= @recipe.step_text %> -
    -
    -
    -
    - -
    - -
    -
    - - <%= link_to 'Edit', edit_log_path(@log), class: 'btn btn-default' %> - <%= link_to 'Back', logs_path, class: 'btn btn-default' %> - -
    -
    - diff --git a/app/views/logs/show.json.jbuilder b/app/views/logs/show.json.jbuilder new file mode 100644 index 0000000..4498db8 --- /dev/null +++ b/app/views/logs/show.json.jbuilder @@ -0,0 +1,6 @@ + +json.extract! @log, :id, :date, :rating, :notes + +json.recipe do + json.partial! 'recipes/recipe', recipe: @log.recipe +end \ No newline at end of file diff --git a/app/views/notes/_form.html.erb b/app/views/notes/_form.html.erb deleted file mode 100644 index fbb7551..0000000 --- a/app/views/notes/_form.html.erb +++ /dev/null @@ -1,13 +0,0 @@ -<%= form_for(note) do |f| %> - <%= render partial: 'shared/error_list', locals: {model: @note} %> - -
    - <%= f.label :content, 'Note', class: 'control-label' %> - <%= f.text_field :content, class: 'form-control' %> -
    - -
    - <%= f.submit class: 'btn btn-primary' %> - <%= link_to 'Back', notes_path, class: 'btn btn-default' %> -
    -<% end %> diff --git a/app/views/notes/edit.html.erb b/app/views/notes/edit.html.erb deleted file mode 100644 index e20934a..0000000 --- a/app/views/notes/edit.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    - - - <%= render 'form', note: @note %> - -
    -
    - diff --git a/app/views/notes/index.html.erb b/app/views/notes/index.html.erb deleted file mode 100644 index 12f72a4..0000000 --- a/app/views/notes/index.html.erb +++ /dev/null @@ -1,47 +0,0 @@ -
    -
    - - - <% if @notes.empty? %> -

    No Notes

    - <% else %> - - <%= link_to 'New Note', new_note_path, class: 'btn btn-default' %> - -
    - - - - - - - - - - - - <% decorate(@notes, NoteDecorator).each do |note| %> - - - - - - <% end %> - -
    NoteDate
    <%= note.content %><%= note.created_at %> - <%= link_to note, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %> - - <% end %> -
    - <% end %> - -
    - - <%= link_to 'New Note', new_note_path, class: 'btn btn-default' %> - - -
    -
    - diff --git a/app/views/notes/new.html.erb b/app/views/notes/new.html.erb deleted file mode 100644 index ee95407..0000000 --- a/app/views/notes/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    - - - <%= render 'form', note: @note %> - -
    -
    - diff --git a/app/views/notes/show.html.erb b/app/views/notes/show.html.erb deleted file mode 100644 index b711b58..0000000 --- a/app/views/notes/show.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -
    -
    - - -

    - <%= @note.content %> -

    - - <%= link_to 'Back', notes_path, class: 'btn btn-default' %> - -
    -
    - diff --git a/app/views/recipes/_editor.html.erb b/app/views/recipes/_editor.html.erb deleted file mode 100644 index d7bc818..0000000 --- a/app/views/recipes/_editor.html.erb +++ /dev/null @@ -1,101 +0,0 @@ - -
    - -
    -
    -
    - <%= f.label :name, class: "control-label" %> - <%= f.text_field :name, class: 'form-control' %> -
    -
    - -
    -
    - <%= f.label :source, class: "control-label" %> - <%= f.text_field :source, class: 'form-control' %> -
    -
    -
    - - -
    - <%= f.label :description, class: "control-label" %> - <%= f.text_area :description, class: 'form-control' %> -
    - -
    - <%= f.label :tag_names, class: "control-label" %> - <%= f.select :tag_names, options_for_select(f.object.tag_names), {}, multiple: true, class: 'form-control tag_names' %> -
    - -
    -
    -
    - <%= f.label :yields, class: "control-label" %> - <%= f.text_field :yields, class: 'form-control', placeholder: 'Servings' %> -
    -
    -
    -
    - <%= f.label :total_time, class: "control-label" %> - <%= f.number_field :total_time, class: 'form-control', placeholder: 'Minutes' %> -
    -
    -
    -
    - <%= f.label :active_time, class: "control-label" %> - <%= f.number_field :active_time, class: 'form-control', placeholder: 'Minutes' %> -
    -
    -
    - - -

    Ingredients

    - -
    - <%= f.fields_for :recipe_ingredients do |ri_form| %> - <%= render partial: 'recipes/editor/ingredient', locals: { f: ri_form } %> - <% end %> -
    - -
    - - <%= link_to_add_association 'Add Ingredient', f, :recipe_ingredients, partial: 'recipes/editor/ingredient', :'data-association-insertion-node' => '#ingredient-list', :'data-association-insertion-method' => 'append', class: 'btn btn-primary', id: 'addIngredientButton' %> - - -

    Steps

    -
    -
    - <%= f.text_area :step_text, class: 'form-control', rows: 15 %> -
    -
    - - <% content_for(:page_bottom) do %> - - <%= render partial: 'recipes/editor/bulk_ingredient_dialog' %> - - - - <% end %> - -
    \ No newline at end of file diff --git a/app/views/recipes/_form.html.erb b/app/views/recipes/_form.html.erb deleted file mode 100644 index 9f9f31b..0000000 --- a/app/views/recipes/_form.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -<%= form_for(@recipe, {html: {class: 'recipe-form'}}) do |f| %> - - <%= render partial: 'shared/error_list', locals: {model: @recipe} %> - - <%= render partial: 'editor', locals: {f: f} %> -

    - -
    - <%= f.submit class: 'btn btn-primary' %> -
    -<% end %> diff --git a/app/views/recipes/_recipe.json.jbuilder b/app/views/recipes/_recipe.json.jbuilder new file mode 100644 index 0000000..317fa21 --- /dev/null +++ b/app/views/recipes/_recipe.json.jbuilder @@ -0,0 +1,32 @@ + + +json.extract! recipe, :id, :name, :rating, :yields, :total_time, :active_time, :created_at, :updated_at, :step_text, :converted_scale, :converted_system, :converted_unit + +json.rendered_steps MarkdownProcessor.render(recipe.step_text) + +json.tags recipe.tag_names + +json.ingredients recipe.recipe_ingredients do |ri| + json.extract! ri, :id, :ingredient_id, :display_name, :name, :quantity, :units, :preparation, :sort_order + + json.ingredient do + if ri.ingredient.nil? + json.null! + else + json.extract! ri.ingredient, :id, :name, :density, :notes + end + end + + json._destroy false +end + +json.nutrition_data do + json.errors recipe.nutrition_data.errors + + json.nutrients NutritionData::NUTRIENTS.select { |_, v| v.present? } do |name, label| + json.name name + json.label label + json.value recipe.nutrition_data.send(name) + end + +end \ No newline at end of file diff --git a/app/views/recipes/edit.html.erb b/app/views/recipes/edit.html.erb deleted file mode 100644 index 4ba4b0c..0000000 --- a/app/views/recipes/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - -

    Editing Recipe

    - - <%= render 'form' %> - - <%= link_to 'Show', @recipe, class: 'btn btn-primary' %> | - <%= link_to 'Back', recipes_path, class: 'btn btn-primary' %> - -
    -
    \ No newline at end of file diff --git a/app/views/recipes/editor/_bulk_ingredient_dialog.html.erb b/app/views/recipes/editor/_bulk_ingredient_dialog.html.erb deleted file mode 100644 index c8b9da4..0000000 --- a/app/views/recipes/editor/_bulk_ingredient_dialog.html.erb +++ /dev/null @@ -1,48 +0,0 @@ - \ No newline at end of file diff --git a/app/views/recipes/editor/_bulk_step_dialog.html.erb b/app/views/recipes/editor/_bulk_step_dialog.html.erb deleted file mode 100644 index e407abb..0000000 --- a/app/views/recipes/editor/_bulk_step_dialog.html.erb +++ /dev/null @@ -1,46 +0,0 @@ - \ No newline at end of file diff --git a/app/views/recipes/editor/_conversion_form.html.erb b/app/views/recipes/editor/_conversion_form.html.erb deleted file mode 100644 index 0841085..0000000 --- a/app/views/recipes/editor/_conversion_form.html.erb +++ /dev/null @@ -1,48 +0,0 @@ - - - - diff --git a/app/views/recipes/editor/_ingredient.html.erb b/app/views/recipes/editor/_ingredient.html.erb deleted file mode 100644 index ae4ab29..0000000 --- a/app/views/recipes/editor/_ingredient.html.erb +++ /dev/null @@ -1,57 +0,0 @@ -
    - - -
    -
    - -
    - -
    -
    - <%= f.label :quantity, class: "control-label" %> - <%= f.text_field :quantity, class: 'form-control quantity' %> -
    -
    - -
    -
    - <%= f.label :units, class: "control-label" %> -
    - <%= f.text_field :units, class: 'form-control units' %> - - - -
    -
    -
    - -
    -
    - <%= f.label :name, class: "control-label" %> - <%= f.text_field :name, class: 'form-control ingredient-typeahead custom' %> -
    -
    - -
    -
    - <%= f.label :preparation, class: "control-label" %> - <%= f.text_field :preparation, class: 'form-control preparation' %> -
    -
    - -
    -
    - -
    - <%= link_to_remove_association f, class: 'remove-button' do %> - - <% end %> -
    -
    - - <%= f.hidden_field :ingredient_id, class: 'ingredient_id' %> - <%= f.hidden_field :sort_order, class: 'sort_order' %> - -
    \ No newline at end of file diff --git a/app/views/recipes/editor/_step.html.erb b/app/views/recipes/editor/_step.html.erb deleted file mode 100644 index 533ff90..0000000 --- a/app/views/recipes/editor/_step.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -
    - - -
    -
    - <%= f.object ? f.object.sort_order : '' %> -
    - -
    -
    - <%= f.text_area :step, class: 'form-control step' %> -
    -
    - -
    - <%= link_to_remove_association f, class: 'remove-button' do %> - - <% end %> -
    -
    - - <%= f.hidden_field :sort_order, class: 'sort_order' %> - - -
    \ No newline at end of file diff --git a/app/views/recipes/index.html.erb b/app/views/recipes/index.html.erb deleted file mode 100644 index dc83ce2..0000000 --- a/app/views/recipes/index.html.erb +++ /dev/null @@ -1,96 +0,0 @@ -
    -
    - - - - <% if current_user? %> - <%= link_to 'New Recipe', new_recipe_path, class: 'btn btn-default' %>
    - <% end %> - - <%= paginate @recipes %> - - <%= form_for(@criteria, as: :criteria, url: recipes_path, method: :get, html: {id: 'search_form'}) do |f| %> - <%= f.hidden_field :sort_column %> - <%= f.hidden_field :sort_direction %> - <%= f.hidden_field :page %> - <%= f.hidden_field :per %> - <%= f.hidden_field :name %> - <%= f.hidden_field :tags %> - <% end %> - -
    - - - - - - - - - - - - - - - - - - - - - - - - <% decorate(@recipes, RecipeDecorator).each do |recipe| %> - - - - - - - - - - - <% end %> - -
    <%= index_sort_header('Name', :name, @criteria) %>Tags<%= index_sort_header('Rating', :rating, @criteria) %>Yields<%= index_sort_header('Time', :total_time, @criteria) %><%= index_sort_header('Created', :created_at, @criteria) %>
    <%= text_field_tag('name_search', @criteria.name) %><%= text_field_tag('tags_search', @criteria.tags) %>
    <%= link_to recipe.short_name, recipe %>
    <%= recipe.tag_names %>
    - <% if recipe.rating %> - <%= text_field_tag('rating', recipe.rating, disabled: true, data: {rating: true, size: '20px', interval: '0.25'}) %> - <% else %> - -- - <% end %> - <%= recipe.yields %><%= recipe_time(recipe) %><%= recipe.created_at %> - <% if current_user? %> -
    - <%= link_to new_recipe_log_path(recipe), class: 'btn btn-xs btn-primary' do %> - - <% end %> - <%= link_to edit_recipe_path(recipe), class: 'btn btn-xs btn-primary' do %> - - <% end %> - <%= link_to recipe, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %> - - <% end %> -
    - <% end %> -
    -
    - - <% if @recipes.empty? %> -

    No Recipes

    - <% end %> - - <%= paginate @recipes %> - -
    - - <% if current_user? %> - <%= link_to 'New Recipe', new_recipe_path, class: 'btn btn-default' %>
    - <% end %> - -
    -
    diff --git a/app/views/recipes/index.json.jbuilder b/app/views/recipes/index.json.jbuilder new file mode 100644 index 0000000..a415406 --- /dev/null +++ b/app/views/recipes/index.json.jbuilder @@ -0,0 +1,13 @@ + +json.cache_root! [Recipe.all, @recipes] do + + json.extract! @recipes, :total_count, :total_pages, :current_page + json.page_size @recipes.limit_value + + json.recipes @recipes do |r| + json.extract! r, :id, :name, :rating, :yields, :total_time, :active_time, :created_at, :updated_at + + json.tags r.tag_names + end + +end \ No newline at end of file diff --git a/app/views/recipes/new.html.erb b/app/views/recipes/new.html.erb deleted file mode 100644 index ba2bb67..0000000 --- a/app/views/recipes/new.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - - - <%= render 'form' %> - - <%= link_to 'Back', recipes_path, class: 'btn btn-default' %> - -
    -
    \ No newline at end of file diff --git a/app/views/recipes/show.html.erb b/app/views/recipes/show.html.erb deleted file mode 100644 index 585bb6a..0000000 --- a/app/views/recipes/show.html.erb +++ /dev/null @@ -1,203 +0,0 @@ - -
    - -
    -
    - -

    - <%= @recipe.description %> -

    -
    -
    - -
    - <% if @recipe.total_time.present? || @recipe.active_time.present? %> -
    -

    <%= recipe_time(@recipe) %>

    -
    - <% end %> - - <% if @recipe.yields.present? %> -
    -

    Yields

    <%= @recipe.yields %>

    -
    - <% end %> - - <% if @recipe.source.present? %> -
    -

    Source

    <%= @recipe.source_markup %>

    -
    - <% end %> - -
    - -
    -
    -
    -
    - -
    -

    Ingredients

    - -
    - - -
    -
    -
      - <% @recipe.recipe_ingredients.each do |i| %> -
    • <%= i.display_name %>
    • - <% end %> -
    -
    -
    - -
    - -
    -
    -
    -

    Directions

    -
    -
    - <%= @recipe.step_text %> -
    -
    -
    -
    - -
    -
    -
    - -
    - <% decorate(@recipe.nutrition_data, NutritionDataDecorator) do |nutrition_data| %> - - - - - <% if @recipe.parsed_yield %> - - <% end %> - - - - - <% NutritionData::NUTRIENTS.select { |_, v| v.present? }.each do |k, v| %> - <%= nutrient_row(@recipe, nutrition_data, v, k) %> - <% end %> -
    Item<%= @recipe.parsed_yield.label %>Total
    - <% end %> - -

    Nutrition Calculation Warnings

    - -
      - <% @recipe.nutrition_data.errors.each do |err| %> -
    • <%= err %>
    • - <% end %> -
    -
    -
    -
    -
    - -
    - -
    -
    - - <%= link_to 'Edit', edit_recipe_path(@recipe), class: 'btn btn-default' %> - <%= link_to 'Back', recipes_path, class: 'btn btn-default' %> - -
    -
    - - \ No newline at end of file diff --git a/app/views/recipes/show.json.jbuilder b/app/views/recipes/show.json.jbuilder new file mode 100644 index 0000000..084c7fe --- /dev/null +++ b/app/views/recipes/show.json.jbuilder @@ -0,0 +1,5 @@ +json.cache_root! [@recipe] do + + json.partial! 'recipe', recipe: @recipe + +end diff --git a/app/views/shared/_error_list.html.erb b/app/views/shared/_error_list.html.erb deleted file mode 100644 index 8ad7638..0000000 --- a/app/views/shared/_error_list.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -<% if model.errors.any? %> -
    -

    <%= pluralize(model.errors.count, 'error') %> prohibited this <%= model.model_name.human %> from being saved:

    - - -
    -<% end %> \ No newline at end of file diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb deleted file mode 100644 index 621dd1f..0000000 --- a/app/views/users/_form.html.erb +++ /dev/null @@ -1,30 +0,0 @@ -<%= form_for(@user, url: user_path) do |f| %> - - <%= render partial: 'shared/error_list', locals: {model: @user} %> - -
    - <%= f.label :username, class: 'control-label' %> - <%= f.text_field :username, autofocus: true, class: 'form-control' %> -
    -
    - <%= f.label :full_name, 'Name', class: 'control-label' %> - <%= f.text_field :full_name, class: 'form-control' %> -
    -
    - <%= f.label :email, class: 'control-label' %> - <%= f.text_field :email, class: 'form-control' %> -
    -
    - <%= f.label :password, class: 'control-label' %> - <%= f.password_field :password, class: 'form-control' %> -
    -
    - <%= f.label :password_confirmation, class: 'control-label' %> - <%= f.password_field :password_confirmation, class: 'form-control' %> -
    - -
    - <%= f.submit class: 'btn btn-primary' %> - <%= link_to "Back", root_path, class: 'btn btn-default' %> -
    -<% end %> diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb deleted file mode 100644 index d15fd3f..0000000 --- a/app/views/users/edit.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    - - - - <%= render partial: 'form' %> - -
    -
    diff --git a/app/views/users/login.html.erb b/app/views/users/login.html.erb deleted file mode 100644 index c9a58ca..0000000 --- a/app/views/users/login.html.erb +++ /dev/null @@ -1,31 +0,0 @@ - - -
    -
    - - - - - <%= form_tag(login_path, :method => :post) do %> - -
    - <%= label_tag :username, "Username", class: 'control-label' %> - <%= text_field_tag :username, nil, autofocus: true, class: 'form-control' %> -
    - -
    - <%= label_tag :password, "Password", class: 'control-label' %> - <%= password_field_tag :password, nil, class: 'form-control' %> -
    - - <%= submit_tag("Login", class: 'btn btn-primary') %> - <% end %> - -
    - - <%= link_to "Create an Account", new_user_path, class: 'btn btn-default' %> - -
    -
    \ No newline at end of file diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb deleted file mode 100644 index 39b2b52..0000000 --- a/app/views/users/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    - - - - <%= render partial: 'form' %> - -
    -
    diff --git a/app/views/users/show.json.jbuilder b/app/views/users/show.json.jbuilder new file mode 100644 index 0000000..80a357b --- /dev/null +++ b/app/views/users/show.json.jbuilder @@ -0,0 +1,8 @@ + +if current_user + json.extract! current_user, :id, :username, :email, :full_name, :admin + + json.name current_user.display_name +else + json.nil! +end diff --git a/bin/webpack b/bin/webpack new file mode 100755 index 0000000..5f9bd1f --- /dev/null +++ b/bin/webpack @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" +ENV["NODE_ENV"] ||= ENV["RAILS_ENV"] + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "rubygems" +require "bundler/setup" + +require "webpacker" +require "webpacker/webpack_runner" +Webpacker::WebpackRunner.run(ARGV) diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server new file mode 100755 index 0000000..173b327 --- /dev/null +++ b/bin/webpack-dev-server @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" +ENV["NODE_ENV"] ||= ENV["RAILS_ENV"] + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "rubygems" +require "bundler/setup" + +require "webpacker" +require "webpacker/dev_server_runner" +Webpacker::DevServerRunner.run(ARGV) diff --git a/config/application.rb b/config/application.rb index 271484c..a0a37b5 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,6 +1,17 @@ require_relative 'boot' -require 'rails/all' +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +# require "active_job/railtie" +require "active_record/railtie" +# require "active_storage/engine" +require "action_controller/railtie" +require "action_mailer/railtie" +require "action_view/railtie" +# require "action_cable/engine" +# require "sprockets/railtie" +require "rails/test_unit/railtie" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -11,6 +22,11 @@ module Parsley # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + config.load_defaults 5.2 + + if ENV['RAILS_USE_MEMCACHE'] == 'true' + config.cache_store = :mem_cache_store, ENV['RAILS_MEMCACHE_HOST'] || 'memcache' + end # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. diff --git a/config/environments/development.rb b/config/environments/development.rb index e64889c..ae6ca1c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,6 @@ Rails.application.configure do + # Verifies that versions and hashed value of the package contents in the project's package.json + config.webpacker.check_yarn_integrity = true # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on @@ -12,11 +14,16 @@ Rails.application.configure do # Show full error reports. config.consider_all_requests_local = true + memcache_enabled = ENV['RAILS_USE_MEMCACHE'] == 'true' + # Enable/disable caching. By default caching is disabled. - if Rails.root.join('tmp/caching-dev.txt').exist? + if Rails.root.join('tmp/caching-dev.txt').exist? || memcache_enabled config.action_controller.perform_caching = true - config.cache_store = :memory_store + unless memcache_enabled + config.cache_store = :memory_store + end + config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=172800' } @@ -37,13 +44,13 @@ Rails.application.configure do # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true - - # Suppress logger output for asset requests. - config.assets.quiet = true + # # Debug mode disables concatenation and preprocessing of assets. + # # This option may cause significant delays in view rendering with a large + # # number of complex assets. + # config.assets.debug = true + # + # # Suppress logger output for asset requests. + # config.assets.quiet = true # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/environments/docker.rb b/config/environments/docker.rb index 2f71bf1..9251fc1 100644 --- a/config/environments/docker.rb +++ b/config/environments/docker.rb @@ -1,54 +1,88 @@ Rails.application.configure do + # Verifies that versions and hashed value of the package contents in the project's package.json + config.webpacker.check_yarn_integrity = false # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false + # Code is not reloaded between requests. + config.cache_classes = true - # Do not eager load code on boot. - config.eager_load = false + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true - # Show full error reports. - config.consider_all_requests_local = true + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = true + config.action_controller.perform_caching = true - # Enable/disable caching. By default caching is disabled. - if Rails.root.join('tmp/caching-dev.txt').exist? - config.action_controller.perform_caching = true + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - config.cache_store = :memory_store - config.public_file_server.headers = { - 'Cache-Control' => 'public, max-age=172800' - } - else - config.action_controller.perform_caching = false + # # Compress JavaScripts and CSS. + # config.assets.js_compressor = :uglifier + # # config.assets.css_compressor = :sass + # + # # Do not fallback to assets pipeline if a precompiled asset is missed. + # config.assets.compile = false - config.cache_store = :null_store - end + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - # Don't care if the mailer can't send. - config.action_mailer.raise_delivery_errors = false + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "parsley_#{Rails.env}" config.action_mailer.perform_caching = false - # Print deprecation notices to the Rails logger. - config.active_support.deprecation = :log + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false - # Raise an error on page load if there are pending migrations. - config.active_record.migration_error = :page_load + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify - # Suppress logger output for asset requests. - config.assets.quiet = true + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - # config.file_watcher = ActiveSupport::EventedFileUpdateChecker + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false end diff --git a/config/environments/production.rb b/config/environments/production.rb index 2aeda8a..4f675ed 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,6 @@ Rails.application.configure do + # Verifies that versions and hashed value of the package contents in the project's package.json + config.webpacker.check_yarn_integrity = false # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. @@ -18,12 +20,12 @@ Rails.application.configure do # Apache or NGINX already handles this. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier - # config.assets.css_compressor = :sass - - # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + # # Compress JavaScripts and CSS. + # config.assets.js_compressor = :uglifier + # # config.assets.css_compressor = :sass + # + # # Do not fallback to assets pipeline if a precompiled asset is missed. + # config.assets.compile = false # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index f826c11..ba00f96 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1,13 +1,13 @@ # Be sure to restart your server when you modify this file. -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path -# Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -# Rails.application.config.assets.precompile += %w( search.js ) - -Rails.application.config.assets.precompile << Proc.new { |filename, path| %w(.eot .svg .tff .woff .woff2).include?(File.extname(filename)) } \ No newline at end of file +# # Version of your assets, change this if you want to expire all your assets. +# Rails.application.config.assets.version = '1.0' +# +# # Add additional assets to the asset load path +# # Rails.application.config.assets.paths << Emoji.images_path +# +# # Precompile additional assets. +# # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. +# # Rails.application.config.assets.precompile += %w( search.js ) +# +# Rails.application.config.assets.precompile << Proc.new { |filename, path| %w(.eot .svg .tff .woff .woff2).include?(File.extname(filename)) } \ No newline at end of file diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb deleted file mode 100644 index f6e7233..0000000 --- a/config/initializers/new_framework_defaults.rb +++ /dev/null @@ -1,23 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.0 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Rails 5.0 release notes for more info on each option. - -# Enable per-form CSRF tokens. Previous versions had false. -Rails.application.config.action_controller.per_form_csrf_tokens = false - -# Enable origin-checking CSRF mitigation. Previous versions had false. -Rails.application.config.action_controller.forgery_protection_origin_check = false - -# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. -# Previous versions had false. -ActiveSupport.to_time_preserves_timezone = false - -# Require `belongs_to` associations by default. Previous versions had false. -Rails.application.config.active_record.belongs_to_required_by_default = false - -# Do not halt callback chains when a callback returns false. Previous versions had true. -ActiveSupport.halt_callback_chains_on_return_false = true diff --git a/config/routes.rb b/config/routes.rb index 1bee66c..dcd6d3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,12 +2,15 @@ Rails.application.routes.draw do resources :notes resources :recipes do + collection do + post :preview_steps + end resources :logs, only: [:new, :create] end resources :logs, except: [:new, :create] - resources :ingredients, except: [:show] do + resources :ingredients, except: [] do collection do get :usda_food_search @@ -35,6 +38,7 @@ Rails.application.routes.draw do get '/login' => 'users#login', as: :login post '/login' => 'users#verify_login' get '/logout' => 'users#logout', as: :logout + get '/user' => 'users#show', as: :show get '/about' => 'home#about', as: :about @@ -48,7 +52,8 @@ Rails.application.routes.draw do resources :users, except: [:new, :create] end - root 'recipes#index' + get 'sw.js' => 'home#sw' + root 'home#index' end diff --git a/config/webpack/development.js b/config/webpack/development.js new file mode 100644 index 0000000..81269f6 --- /dev/null +++ b/config/webpack/development.js @@ -0,0 +1,3 @@ +const environment = require('./environment') + +module.exports = environment.toWebpackConfig() diff --git a/config/webpack/environment.js b/config/webpack/environment.js new file mode 100644 index 0000000..841c7df --- /dev/null +++ b/config/webpack/environment.js @@ -0,0 +1,11 @@ +const { environment } = require('@rails/webpacker'); +const vue = require('./loaders/vue'); +const svg = require('./loaders/svg'); + +environment.loaders.append('vue', vue); +environment.loaders.append('svg', svg); + +const fileLoader = environment.loaders.get('file'); +fileLoader.exclude = /\.(svg)$/i; + +module.exports = environment; diff --git a/config/webpack/loaders/svg.js b/config/webpack/loaders/svg.js new file mode 100644 index 0000000..16b6182 --- /dev/null +++ b/config/webpack/loaders/svg.js @@ -0,0 +1,7 @@ + +module.exports = { + test: /\.svg$/, + use: [{ + loader: 'svg-loader' + }] +}; diff --git a/config/webpack/loaders/vue.js b/config/webpack/loaders/vue.js new file mode 100644 index 0000000..4576b03 --- /dev/null +++ b/config/webpack/loaders/vue.js @@ -0,0 +1,13 @@ +const { dev_server: devServer } = require('@rails/webpacker').config + +const isProduction = process.env.NODE_ENV === 'production' +const inDevServer = process.argv.find(v => v.includes('webpack-dev-server')) +const extractCSS = !(inDevServer && (devServer && devServer.hmr)) || isProduction + +module.exports = { + test: /\.vue(\.erb)?$/, + use: [{ + loader: 'vue-loader', + options: { extractCSS } + }] +} diff --git a/config/webpack/production.js b/config/webpack/production.js new file mode 100644 index 0000000..81269f6 --- /dev/null +++ b/config/webpack/production.js @@ -0,0 +1,3 @@ +const environment = require('./environment') + +module.exports = environment.toWebpackConfig() diff --git a/config/webpack/test.js b/config/webpack/test.js new file mode 100644 index 0000000..81269f6 --- /dev/null +++ b/config/webpack/test.js @@ -0,0 +1,3 @@ +const environment = require('./environment') + +module.exports = environment.toWebpackConfig() diff --git a/config/webpacker.yml b/config/webpacker.yml new file mode 100644 index 0000000..9e5e0a9 --- /dev/null +++ b/config/webpacker.yml @@ -0,0 +1,69 @@ +# Note: You must restart bin/webpack-dev-server for changes to take effect + +default: &default + source_path: app/javascript + source_entry_path: packs + public_output_path: packs + cache_path: tmp/cache/webpacker + + # Additional paths webpack should lookup modules + # ['app/assets', 'engine/foo/app/assets'] + resolved_paths: [] + + # Reload manifest.json on all requests so we reload latest compiled packs + cache_manifest: false + + extensions: + - .vue + - .js + - .sass + - .scss + - .css + - .module.sass + - .module.scss + - .module.css + - .png + - .svg + - .gif + - .jpeg + - .jpg + +development: + <<: *default + compile: true + + # Reference: https://webpack.js.org/configuration/dev-server/ + dev_server: + https: false + host: localhost + port: 3035 + public: localhost:3035 + hmr: true + # Inline should be set to true if using HMR + inline: true + overlay: true + compress: true + disable_host_check: true + use_local_ip: false + quiet: false + headers: + 'Access-Control-Allow-Origin': '*' + watch_options: + ignored: /node_modules/ + + +test: + <<: *default + compile: true + + # Compile test packs to a separate directory + public_output_path: packs-test + +production: + <<: *default + + # Production depends on precompilation of packs prior to booting for performance. + compile: false + + # Cache manifest.json for performance + cache_manifest: true diff --git a/db/schema.rb b/db/schema.rb index d39c6a9..84ae75c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,105 +10,105 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170414233856) do +ActiveRecord::Schema.define(version: 2017_04_14_233856) do create_table "ingredient_units", force: :cascade do |t| - t.integer "ingredient_id", null: false - t.string "name", null: false - t.decimal "gram_weight", precision: 10, scale: 2, null: false + t.integer "ingredient_id", null: false + t.string "name", null: false + t.decimal "gram_weight", precision: 10, scale: 2, null: false t.index ["ingredient_id"], name: "index_ingredient_units_on_ingredient_id" end create_table "ingredients", force: :cascade do |t| - t.string "name" - t.string "density" - t.text "notes" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "ndbn", limit: 5 - t.decimal "water", precision: 10, scale: 2 - t.decimal "protein", precision: 10, scale: 2 - t.decimal "lipids", precision: 10, scale: 2 - t.decimal "ash", precision: 10, scale: 2 - t.decimal "carbohydrates", precision: 10, scale: 2 - t.integer "kcal" - t.decimal "fiber", precision: 10, scale: 1 - t.decimal "sugar", precision: 10, scale: 2 - t.integer "user_id" - t.integer "calcium" - t.decimal "iron", precision: 10, scale: 2 - t.integer "magnesium" - t.integer "phosphorus" - t.integer "potassium" - t.integer "sodium" - t.decimal "zinc", precision: 10, scale: 2 - t.decimal "copper", precision: 10, scale: 3 - t.decimal "manganese", precision: 10, scale: 3 - t.decimal "vit_c", precision: 10, scale: 1 - t.decimal "vit_b6", precision: 10, scale: 3 - t.decimal "vit_b12", precision: 10, scale: 2 - t.integer "vit_a" - t.decimal "vit_e", precision: 10, scale: 2 - t.decimal "vit_d", precision: 10, scale: 1 - t.decimal "vit_k", precision: 10, scale: 1 - t.decimal "cholesterol", precision: 10, scale: 3 + t.string "name" + t.string "density" + t.text "notes" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "ndbn", limit: 5 + t.decimal "water", precision: 10, scale: 2 + t.decimal "protein", precision: 10, scale: 2 + t.decimal "lipids", precision: 10, scale: 2 + t.decimal "ash", precision: 10, scale: 2 + t.decimal "carbohydrates", precision: 10, scale: 2 + t.integer "kcal" + t.decimal "fiber", precision: 10, scale: 1 + t.decimal "sugar", precision: 10, scale: 2 + t.integer "user_id" + t.integer "calcium" + t.decimal "iron", precision: 10, scale: 2 + t.integer "magnesium" + t.integer "phosphorus" + t.integer "potassium" + t.integer "sodium" + t.decimal "zinc", precision: 10, scale: 2 + t.decimal "copper", precision: 10, scale: 3 + t.decimal "manganese", precision: 10, scale: 3 + t.decimal "vit_c", precision: 10, scale: 1 + t.decimal "vit_b6", precision: 10, scale: 3 + t.decimal "vit_b12", precision: 10, scale: 2 + t.integer "vit_a" + t.decimal "vit_e", precision: 10, scale: 2 + t.decimal "vit_d", precision: 10, scale: 1 + t.decimal "vit_k", precision: 10, scale: 1 + t.decimal "cholesterol", precision: 10, scale: 3 end create_table "logs", force: :cascade do |t| - t.integer "user_id" - t.integer "recipe_id" - t.integer "source_recipe_id" + t.integer "user_id" + t.integer "recipe_id" + t.integer "source_recipe_id" t.datetime "date" - t.integer "rating" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.text "notes" + t.integer "rating" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "notes" end create_table "notes", force: :cascade do |t| - t.integer "user_id", null: false - t.text "content", null: false + t.integer "user_id", null: false + t.text "content", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["user_id"], name: "index_notes_on_user_id" end create_table "recipe_ingredients", force: :cascade do |t| - t.integer "ingredient_id" - t.integer "recipe_id" - t.string "name" - t.integer "sort_order" - t.string "quantity" - t.string "units" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.text "preparation" + t.integer "ingredient_id" + t.integer "recipe_id" + t.string "name" + t.integer "sort_order" + t.string "quantity" + t.string "units" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "preparation" t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id" end create_table "recipe_steps", force: :cascade do |t| - t.integer "recipe_id" - t.integer "sort_order" - t.text "step" + t.integer "recipe_id" + t.integer "sort_order" + t.text "step" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["recipe_id"], name: "index_recipe_steps_on_recipe_id" end create_table "recipes", force: :cascade do |t| - t.string "name" - t.text "description" - t.text "source" - t.string "yields" - t.integer "total_time" - t.integer "active_time" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "deleted" - t.integer "user_id" - t.boolean "is_log" - t.float "rating" - t.text "step_text" + t.string "name" + t.text "description" + t.text "source" + t.string "yields" + t.integer "total_time" + t.integer "active_time" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "deleted" + t.integer "user_id" + t.boolean "is_log" + t.float "rating" + t.text "step_text" end create_table "recipes_tags", id: false, force: :cascade do |t| @@ -119,73 +119,73 @@ ActiveRecord::Schema.define(version: 20170414233856) do end create_table "tags", force: :cascade do |t| - t.string "name" - t.string "lowercase_name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "name" + t.string "lowercase_name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["lowercase_name"], name: "index_tags_on_lowercase_name", unique: true end create_table "usda_food_weights", force: :cascade do |t| - t.integer "usda_food_id", null: false - t.decimal "amount", precision: 7, scale: 3 - t.string "description" - t.decimal "gram_weight", precision: 7, scale: 1 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "usda_food_id", null: false + t.decimal "amount", precision: 7, scale: 3 + t.string "description" + t.decimal "gram_weight", precision: 7, scale: 1 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["usda_food_id"], name: "index_usda_food_weights_on_usda_food_id" end create_table "usda_foods", force: :cascade do |t| - t.string "ndbn", limit: 5, null: false - t.string "long_description" - t.string "short_description" - t.decimal "water", precision: 10, scale: 2 - t.integer "kcal" - t.decimal "protein", precision: 10, scale: 2 - t.decimal "lipid", precision: 10, scale: 2 - t.decimal "ash", precision: 10, scale: 2 - t.decimal "carbohydrates", precision: 10, scale: 2 - t.decimal "fiber", precision: 10, scale: 1 - t.decimal "sugar", precision: 10, scale: 2 - t.decimal "gram_weight_1", precision: 9, scale: 2 - t.decimal "gram_weight_2", precision: 9, scale: 2 - t.string "gram_weight_desc_1" - t.string "gram_weight_desc_2" - t.string "refuse_description" - t.integer "refuse_percent" - t.string "scientific_name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "calcium" - t.decimal "iron", precision: 10, scale: 2 - t.integer "magnesium" - t.integer "phosphorus" - t.integer "potassium" - t.integer "sodium" - t.decimal "zinc", precision: 10, scale: 2 - t.decimal "copper", precision: 10, scale: 3 - t.decimal "manganese", precision: 10, scale: 3 - t.decimal "vit_c", precision: 10, scale: 1 - t.decimal "vit_b6", precision: 10, scale: 3 - t.decimal "vit_b12", precision: 10, scale: 2 - t.integer "vit_a" - t.decimal "vit_e", precision: 10, scale: 2 - t.decimal "vit_d", precision: 10, scale: 1 - t.decimal "vit_k", precision: 10, scale: 1 - t.decimal "cholesterol", precision: 10, scale: 3 + t.string "ndbn", limit: 5, null: false + t.string "long_description" + t.string "short_description" + t.decimal "water", precision: 10, scale: 2 + t.integer "kcal" + t.decimal "protein", precision: 10, scale: 2 + t.decimal "lipid", precision: 10, scale: 2 + t.decimal "ash", precision: 10, scale: 2 + t.decimal "carbohydrates", precision: 10, scale: 2 + t.decimal "fiber", precision: 10, scale: 1 + t.decimal "sugar", precision: 10, scale: 2 + t.decimal "gram_weight_1", precision: 9, scale: 2 + t.decimal "gram_weight_2", precision: 9, scale: 2 + t.string "gram_weight_desc_1" + t.string "gram_weight_desc_2" + t.string "refuse_description" + t.integer "refuse_percent" + t.string "scientific_name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "calcium" + t.decimal "iron", precision: 10, scale: 2 + t.integer "magnesium" + t.integer "phosphorus" + t.integer "potassium" + t.integer "sodium" + t.decimal "zinc", precision: 10, scale: 2 + t.decimal "copper", precision: 10, scale: 3 + t.decimal "manganese", precision: 10, scale: 3 + t.decimal "vit_c", precision: 10, scale: 1 + t.decimal "vit_b6", precision: 10, scale: 3 + t.decimal "vit_b12", precision: 10, scale: 2 + t.integer "vit_a" + t.decimal "vit_e", precision: 10, scale: 2 + t.decimal "vit_d", precision: 10, scale: 1 + t.decimal "vit_k", precision: 10, scale: 1 + t.decimal "cholesterol", precision: 10, scale: 3 t.index ["long_description"], name: "index_usda_foods_on_long_description" t.index ["ndbn"], name: "index_usda_foods_on_ndbn" end create_table "users", force: :cascade do |t| - t.string "username" - t.string "email" - t.string "full_name" - t.string "password_digest" - t.boolean "admin" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "username" + t.string "email" + t.string "full_name" + t.string "password_digest" + t.boolean "admin" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end end diff --git a/docker-compose.yml b/docker-compose.yml index af0348a..292c0ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '3' services: postgres: @@ -8,14 +8,21 @@ services: - POSTGRES_USER=parsley volumes: - sql-data:/var/lib/postgresql/data + + memcache: + image: memcached:1.5.4-alpine + command: memcached -m 512 + web: build: . + environment: + - RAILS_USE_MEMCACHE=true + - RAILS_LOG_TO_STDOUT=true ports: - "3000:80" - volumes: - - .:/home/app/parsley/ links: - postgres + volumes: sql-data: driver: local \ No newline at end of file diff --git a/docker/nginx_env.conf b/docker/nginx_env.conf index 4d813f0..4d8e93c 100644 --- a/docker/nginx_env.conf +++ b/docker/nginx_env.conf @@ -1,3 +1,6 @@ +env RAILS_LOG_TO_STDOUT; +env RAILS_USE_MEMCACHE; +env RAILS_MEMCACHE_HOST; env PARSLEY_DB_HOST; env PARSLEY_DB_USER; env PARSLEY_DB_PASSWORD; diff --git a/docker/nginx_server.conf b/docker/nginx_server.conf index d8c86f3..6643427 100644 --- a/docker/nginx_server.conf +++ b/docker/nginx_server.conf @@ -7,7 +7,7 @@ server { passenger_ruby /usr/bin/ruby; - location ~ ^/(assets)/ { + location ~ ^/(assets|packs)/ { gzip_static on; expires max; add_header Cache-Control public; diff --git a/lib/unit_conversion/conversions.rb b/lib/unit_conversion/conversions.rb index 3349fdf..278f622 100644 --- a/lib/unit_conversion/conversions.rb +++ b/lib/unit_conversion/conversions.rb @@ -34,7 +34,12 @@ module UnitConversion input = input / @density.unitwise end - input = input.convert_to @target_unit.unit + begin + input = input.convert_to @target_unit.unit + rescue Unitwise::ConversionError => err + raise ConversionError, err.message + end + formatter = @target_unit.metric? ? DecimalFormatter.new : value_unit.formatter diff --git a/lib/unit_conversion/errors.rb b/lib/unit_conversion/errors.rb index aa7138f..361d500 100644 --- a/lib/unit_conversion/errors.rb +++ b/lib/unit_conversion/errors.rb @@ -7,4 +7,7 @@ module UnitConversion class MissingDensityError < UnparseableUnitError end + + class ConversionError < UnparseableUnitError + end end diff --git a/lib/unit_conversion/parsed_number.rb b/lib/unit_conversion/parsed_number.rb index 2225c1d..1924f2c 100644 --- a/lib/unit_conversion/parsed_number.rb +++ b/lib/unit_conversion/parsed_number.rb @@ -17,6 +17,7 @@ module UnitConversion end def parse_value(str) + str = str.to_s.strip if str =~ /^#{INTEGER_REGEX}$/ str.to_i elsif str =~ /^#{RATIONAL_REGEX}$/ diff --git a/package.json b/package.json new file mode 100644 index 0000000..826bd8d --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "dependencies": { + "@rails/webpacker": "^3.5.3", + "autosize": "^4.0.1", + "bulma": "^0.7.1", + "caniuse-lite": "^1.0.30000815", + "css-loader": "^0.28.11", + "lodash": "^4.17.5", + "open-iconic": "^1.1.1", + "svg-loader": "^0.0.2", + "vue": "^2.5.16", + "vue-loader": "^14.2.2", + "vue-progressbar": "^0.7.4", + "vue-router": "^3.0.1", + "vue-template-compiler": "^2.5.16", + "vuex": "^3.0.1", + "vuex-router-sync": "^5.0.0", + "webpack": "^3.11.0" + }, + "devDependencies": { + "webpack-dev-server": "^2.11.2" + } +} diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..f9729af --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "Parsley", + "short_name": "Parsley", + "start_url": ".", + "display": "standalone", + "background_color": "#4a4a4a", + "description": "A recipe manager." +} \ No newline at end of file diff --git a/spec/factories/ingredient_unit.rb b/spec/factories/ingredient_unit.rb index 74fc05b..c8ba875 100644 --- a/spec/factories/ingredient_unit.rb +++ b/spec/factories/ingredient_unit.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :ingredient_unit do ingredient name 'Each' diff --git a/spec/factories/ingredients.rb b/spec/factories/ingredients.rb index 74caafd..3b04b28 100644 --- a/spec/factories/ingredients.rb +++ b/spec/factories/ingredients.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :ingredient do name 'Ingredient' density nil diff --git a/spec/factories/logs.rb b/spec/factories/logs.rb index dd91c76..0b81d38 100644 --- a/spec/factories/logs.rb +++ b/spec/factories/logs.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :log do date "2016-07-06 20:13:14" rating 1 diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index f771187..a0cd701 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :note do user content "MyText" diff --git a/spec/factories/recipe_ingredients.rb b/spec/factories/recipe_ingredients.rb index b8125b3..e32fa0e 100644 --- a/spec/factories/recipe_ingredients.rb +++ b/spec/factories/recipe_ingredients.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :recipe_ingredient do sort_order 1 end diff --git a/spec/factories/recipes.rb b/spec/factories/recipes.rb index 8334cfa..f134e49 100644 --- a/spec/factories/recipes.rb +++ b/spec/factories/recipes.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :recipe do name "Recipe" description "desc" diff --git a/spec/factories/tags.rb b/spec/factories/tags.rb index d0f0913..d4d2b99 100644 --- a/spec/factories/tags.rb +++ b/spec/factories/tags.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :tag do sequence(:name) { |n| "tag#{n}" } end diff --git a/spec/factories/usda_food_weights.rb b/spec/factories/usda_food_weights.rb index 229c420..acf23c3 100644 --- a/spec/factories/usda_food_weights.rb +++ b/spec/factories/usda_food_weights.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :usda_food_weight do amount 1 diff --git a/spec/factories/usda_foods.rb b/spec/factories/usda_foods.rb index 8867367..16a8f28 100644 --- a/spec/factories/usda_foods.rb +++ b/spec/factories/usda_foods.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do sequence :unique_ndbn do |n| n.to_s.rjust(5, '0') diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 533d54a..c0af4c9 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do sequence :generic_email do |n| "generic_email_#{n.to_s.rjust(3, '0')}@example.com" diff --git a/spec/lib/unit_conversion_spec.rb b/spec/lib/unit_conversion_spec.rb index 8aeb615..2fca711 100644 --- a/spec/lib/unit_conversion_spec.rb +++ b/spec/lib/unit_conversion_spec.rb @@ -89,7 +89,7 @@ RSpec.describe UnitConversion do '1/3 c' => Unitwise("1/3".to_r, 'cup'), '-5/16 g' => Unitwise("-5/16".to_r, 'gram'), '2 1/2 c' => Unitwise("5/2".to_r, 'cup'), - '1.03 gram/ml' => Unitwise(1.03, 'gram/milliliter') + '1.03 gram/ml' => Unitwise(1.03, 'g/ml') } data.each do |input, output| diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 138db18..553b60b 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -29,7 +29,7 @@ ActiveRecord::Migration.maintain_test_schema! RSpec.configure do |config| - config.include FactoryGirl::Syntax::Methods + config.include FactoryBot::Syntax::Methods config.before(:suite) do DatabaseCleaner.strategy = :transaction @@ -38,7 +38,7 @@ RSpec.configure do |config| DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.cleaning do - FactoryGirl.lint + FactoryBot.lint end end end diff --git a/vendor/assets/fonts/inconsolata-bold.woff2 b/vendor/assets/fonts/inconsolata-bold.woff2 deleted file mode 100644 index 2e0f282..0000000 Binary files a/vendor/assets/fonts/inconsolata-bold.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/inconsolata.woff2 b/vendor/assets/fonts/inconsolata.woff2 deleted file mode 100644 index 1347eb5..0000000 Binary files a/vendor/assets/fonts/inconsolata.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/news-cycle-bold.woff2 b/vendor/assets/fonts/news-cycle-bold.woff2 deleted file mode 100644 index 864e145..0000000 Binary files a/vendor/assets/fonts/news-cycle-bold.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/news-cycle.woff2 b/vendor/assets/fonts/news-cycle.woff2 deleted file mode 100644 index 0762465..0000000 Binary files a/vendor/assets/fonts/news-cycle.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/open-sans-bold-italic.woff2 b/vendor/assets/fonts/open-sans-bold-italic.woff2 deleted file mode 100644 index 646a4f7..0000000 Binary files a/vendor/assets/fonts/open-sans-bold-italic.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/open-sans-bold.woff2 b/vendor/assets/fonts/open-sans-bold.woff2 deleted file mode 100644 index 51c75ca..0000000 Binary files a/vendor/assets/fonts/open-sans-bold.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/open-sans-italic.woff2 b/vendor/assets/fonts/open-sans-italic.woff2 deleted file mode 100644 index 005b450..0000000 Binary files a/vendor/assets/fonts/open-sans-italic.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/open-sans-light-italic.woff2 b/vendor/assets/fonts/open-sans-light-italic.woff2 deleted file mode 100644 index b10b85b..0000000 Binary files a/vendor/assets/fonts/open-sans-light-italic.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/open-sans-light.woff2 b/vendor/assets/fonts/open-sans-light.woff2 deleted file mode 100644 index cf3ec55..0000000 Binary files a/vendor/assets/fonts/open-sans-light.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/open-sans.woff2 b/vendor/assets/fonts/open-sans.woff2 deleted file mode 100644 index 402dfd7..0000000 Binary files a/vendor/assets/fonts/open-sans.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/raleway-bold.woff2 b/vendor/assets/fonts/raleway-bold.woff2 deleted file mode 100644 index 885b599..0000000 Binary files a/vendor/assets/fonts/raleway-bold.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/raleway.woff2 b/vendor/assets/fonts/raleway.woff2 deleted file mode 100644 index 413ce60..0000000 Binary files a/vendor/assets/fonts/raleway.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/roboto-bold.woff2 b/vendor/assets/fonts/roboto-bold.woff2 deleted file mode 100644 index 3b2dd4e..0000000 Binary files a/vendor/assets/fonts/roboto-bold.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/roboto-light.woff2 b/vendor/assets/fonts/roboto-light.woff2 deleted file mode 100644 index 4411cbc..0000000 Binary files a/vendor/assets/fonts/roboto-light.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/roboto-medium.woff2 b/vendor/assets/fonts/roboto-medium.woff2 deleted file mode 100644 index 6be92c7..0000000 Binary files a/vendor/assets/fonts/roboto-medium.woff2 and /dev/null differ diff --git a/vendor/assets/fonts/roboto.woff2 b/vendor/assets/fonts/roboto.woff2 deleted file mode 100644 index d1035f9..0000000 Binary files a/vendor/assets/fonts/roboto.woff2 and /dev/null differ diff --git a/vendor/assets/images/chosen-sprite.png b/vendor/assets/images/chosen-sprite.png deleted file mode 100644 index c57da70..0000000 Binary files a/vendor/assets/images/chosen-sprite.png and /dev/null differ diff --git a/vendor/assets/images/chosen-sprite@2x.png b/vendor/assets/images/chosen-sprite@2x.png deleted file mode 100644 index 6b50545..0000000 Binary files a/vendor/assets/images/chosen-sprite@2x.png and /dev/null differ diff --git a/vendor/assets/javascripts/.keep b/vendor/assets/javascripts/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/vendor/assets/javascripts/autosize.js b/vendor/assets/javascripts/autosize.js deleted file mode 100644 index cfa49e7..0000000 --- a/vendor/assets/javascripts/autosize.js +++ /dev/null @@ -1,243 +0,0 @@ -/*! - Autosize 3.0.14 - license: MIT - http://www.jacklmoore.com/autosize -*/ -(function (global, factory) { - if (typeof define === 'function' && define.amd) { - define(['exports', 'module'], factory); - } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { - factory(exports, module); - } else { - var mod = { - exports: {} - }; - factory(mod.exports, mod); - global.autosize = mod.exports; - } -})(this, function (exports, module) { - 'use strict'; - - var set = typeof Set === 'function' ? new Set() : (function () { - var list = []; - - return { - has: function has(key) { - return Boolean(list.indexOf(key) > -1); - }, - add: function add(key) { - list.push(key); - }, - 'delete': function _delete(key) { - list.splice(list.indexOf(key), 1); - } }; - })(); - - function assign(ta) { - var _ref = arguments[1] === undefined ? {} : arguments[1]; - - var _ref$setOverflowX = _ref.setOverflowX; - var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX; - var _ref$setOverflowY = _ref.setOverflowY; - var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY; - - if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || set.has(ta)) return; - - var heightOffset = null; - var overflowY = null; - var clientWidth = ta.clientWidth; - - function init() { - var style = window.getComputedStyle(ta, null); - - overflowY = style.overflowY; - - if (style.resize === 'vertical') { - ta.style.resize = 'none'; - } else if (style.resize === 'both') { - ta.style.resize = 'horizontal'; - } - - if (style.boxSizing === 'content-box') { - heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)); - } else { - heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); - } - // Fix when a textarea is not on document body and heightOffset is Not a Number - if (isNaN(heightOffset)) { - heightOffset = 0; - } - - update(); - } - - function changeOverflow(value) { - { - // Chrome/Safari-specific fix: - // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space - // made available by removing the scrollbar. The following forces the necessary text reflow. - var width = ta.style.width; - ta.style.width = '0px'; - // Force reflow: - /* jshint ignore:start */ - ta.offsetWidth; - /* jshint ignore:end */ - ta.style.width = width; - } - - overflowY = value; - - if (setOverflowY) { - ta.style.overflowY = value; - } - - resize(); - } - - function resize() { - var htmlTop = window.pageYOffset; - var bodyTop = document.body.scrollTop; - var originalHeight = ta.style.height; - - ta.style.height = 'auto'; - - var endHeight = ta.scrollHeight + heightOffset; - - if (ta.scrollHeight === 0) { - // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. - ta.style.height = originalHeight; - return; - } - - ta.style.height = endHeight + 'px'; - - // used to check if an update is actually necessary on window.resize - clientWidth = ta.clientWidth; - - // prevents scroll-position jumping - document.documentElement.scrollTop = htmlTop; - document.body.scrollTop = bodyTop; - } - - function update() { - var startHeight = ta.style.height; - - resize(); - - var style = window.getComputedStyle(ta, null); - - if (style.height !== ta.style.height) { - if (overflowY !== 'visible') { - changeOverflow('visible'); - } - } else { - if (overflowY !== 'hidden') { - changeOverflow('hidden'); - } - } - - if (startHeight !== ta.style.height) { - var evt = document.createEvent('Event'); - evt.initEvent('autosize:resized', true, false); - ta.dispatchEvent(evt); - } - } - - var pageResize = function pageResize() { - if (ta.clientWidth !== clientWidth) { - update(); - } - }; - - var destroy = (function (style) { - window.removeEventListener('resize', pageResize, false); - ta.removeEventListener('input', update, false); - ta.removeEventListener('keyup', update, false); - ta.removeEventListener('autosize:destroy', destroy, false); - ta.removeEventListener('autosize:update', update, false); - set['delete'](ta); - - Object.keys(style).forEach(function (key) { - ta.style[key] = style[key]; - }); - }).bind(ta, { - height: ta.style.height, - resize: ta.style.resize, - overflowY: ta.style.overflowY, - overflowX: ta.style.overflowX, - wordWrap: ta.style.wordWrap }); - - ta.addEventListener('autosize:destroy', destroy, false); - - // IE9 does not fire onpropertychange or oninput for deletions, - // so binding to onkeyup to catch most of those events. - // There is no way that I know of to detect something like 'cut' in IE9. - if ('onpropertychange' in ta && 'oninput' in ta) { - ta.addEventListener('keyup', update, false); - } - - window.addEventListener('resize', pageResize, false); - ta.addEventListener('input', update, false); - ta.addEventListener('autosize:update', update, false); - set.add(ta); - - if (setOverflowX) { - ta.style.overflowX = 'hidden'; - ta.style.wordWrap = 'break-word'; - } - - init(); - } - - function destroy(ta) { - if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; - var evt = document.createEvent('Event'); - evt.initEvent('autosize:destroy', true, false); - ta.dispatchEvent(evt); - } - - function update(ta) { - if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; - var evt = document.createEvent('Event'); - evt.initEvent('autosize:update', true, false); - ta.dispatchEvent(evt); - } - - var autosize = null; - - // Do nothing in Node.js environment and IE8 (or lower) - if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') { - autosize = function (el) { - return el; - }; - autosize.destroy = function (el) { - return el; - }; - autosize.update = function (el) { - return el; - }; - } else { - autosize = function (el, options) { - if (el) { - Array.prototype.forEach.call(el.length ? el : [el], function (x) { - return assign(x, options); - }); - } - return el; - }; - autosize.destroy = function (el) { - if (el) { - Array.prototype.forEach.call(el.length ? el : [el], destroy); - } - return el; - }; - autosize.update = function (el) { - if (el) { - Array.prototype.forEach.call(el.length ? el : [el], update); - } - return el; - }; - } - - module.exports = autosize; -}); \ No newline at end of file diff --git a/vendor/assets/javascripts/bootstrap-datepicker.js b/vendor/assets/javascripts/bootstrap-datepicker.js deleted file mode 100644 index d2faff6..0000000 --- a/vendor/assets/javascripts/bootstrap-datepicker.js +++ /dev/null @@ -1,2089 +0,0 @@ -/*! - * Datepicker for Bootstrap v1.6.1 (https://github.com/eternicode/bootstrap-datepicker) - * - * Copyright 2012 Stefan Petre - * Improvements by Andrew Rowls - * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - */(function(factory){ - if (typeof define === "function" && define.amd) { - define(["jquery"], factory); - } else if (typeof exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } -}(function($, undefined){ - - function UTCDate(){ - return new Date(Date.UTC.apply(Date, arguments)); - } - function UTCToday(){ - var today = new Date(); - return UTCDate(today.getFullYear(), today.getMonth(), today.getDate()); - } - function isUTCEquals(date1, date2) { - return ( - date1.getUTCFullYear() === date2.getUTCFullYear() && - date1.getUTCMonth() === date2.getUTCMonth() && - date1.getUTCDate() === date2.getUTCDate() - ); - } - function alias(method){ - return function(){ - return this[method].apply(this, arguments); - }; - } - function isValidDate(d) { - return d && !isNaN(d.getTime()); - } - - var DateArray = (function(){ - var extras = { - get: function(i){ - return this.slice(i)[0]; - }, - contains: function(d){ - // Array.indexOf is not cross-browser; - // $.inArray doesn't work with Dates - var val = d && d.valueOf(); - for (var i=0, l=this.length; i < l; i++) - if (this[i].valueOf() === val) - return i; - return -1; - }, - remove: function(i){ - this.splice(i,1); - }, - replace: function(new_array){ - if (!new_array) - return; - if (!$.isArray(new_array)) - new_array = [new_array]; - this.clear(); - this.push.apply(this, new_array); - }, - clear: function(){ - this.length = 0; - }, - copy: function(){ - var a = new DateArray(); - a.replace(this); - return a; - } - }; - - return function(){ - var a = []; - a.push.apply(a, arguments); - $.extend(a, extras); - return a; - }; - })(); - - - // Picker object - - var Datepicker = function(element, options){ - $(element).data('datepicker', this); - this._process_options(options); - - this.dates = new DateArray(); - this.viewDate = this.o.defaultViewDate; - this.focusDate = null; - - this.element = $(element); - this.isInput = this.element.is('input'); - this.inputField = this.isInput ? this.element : this.element.find('input'); - this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .btn') : false; - this.hasInput = this.component && this.inputField.length; - if (this.component && this.component.length === 0) - this.component = false; - this.isInline = !this.component && this.element.is('div'); - - this.picker = $(DPGlobal.template); - - // Checking templates and inserting - if (this._check_template(this.o.templates.leftArrow)) { - this.picker.find('.prev').html(this.o.templates.leftArrow); - } - if (this._check_template(this.o.templates.rightArrow)) { - this.picker.find('.next').html(this.o.templates.rightArrow); - } - - this._buildEvents(); - this._attachEvents(); - - if (this.isInline){ - this.picker.addClass('datepicker-inline').appendTo(this.element); - } - else { - this.picker.addClass('datepicker-dropdown dropdown-menu'); - } - - if (this.o.rtl){ - this.picker.addClass('datepicker-rtl'); - } - - this.viewMode = this.o.startView; - - if (this.o.calendarWeeks) - this.picker.find('thead .datepicker-title, tfoot .today, tfoot .clear') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - - this._allow_update = false; - - this.setStartDate(this._o.startDate); - this.setEndDate(this._o.endDate); - this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled); - this.setDaysOfWeekHighlighted(this.o.daysOfWeekHighlighted); - this.setDatesDisabled(this.o.datesDisabled); - - this.fillDow(); - this.fillMonths(); - - this._allow_update = true; - - this.update(); - this.showMode(); - - if (this.isInline){ - this.show(); - } - }; - - Datepicker.prototype = { - constructor: Datepicker, - - _resolveViewName: function(view, default_value){ - if (view === 0 || view === 'days' || view === 'month') { - return 0; - } - if (view === 1 || view === 'months' || view === 'year') { - return 1; - } - if (view === 2 || view === 'years' || view === 'decade') { - return 2; - } - if (view === 3 || view === 'decades' || view === 'century') { - return 3; - } - if (view === 4 || view === 'centuries' || view === 'millennium') { - return 4; - } - return default_value === undefined ? false : default_value; - }, - - _check_template: function(tmp){ - try { - // If empty - if (tmp === undefined || tmp === "") { - return false; - } - // If no html, everything ok - if ((tmp.match(/[<>]/g) || []).length <= 0) { - return true; - } - // Checking if html is fine - var jDom = $(tmp); - return jDom.length > 0; - } - catch (ex) { - return false; - } - }, - - _process_options: function(opts){ - // Store raw options for reference - this._o = $.extend({}, this._o, opts); - // Processed options - var o = this.o = $.extend({}, this._o); - - // Check if "de-DE" style date is available, if not language should - // fallback to 2 letter code eg "de" - var lang = o.language; - if (!dates[lang]){ - lang = lang.split('-')[0]; - if (!dates[lang]) - lang = defaults.language; - } - o.language = lang; - - // Retrieve view index from any aliases - o.startView = this._resolveViewName(o.startView, 0); - o.minViewMode = this._resolveViewName(o.minViewMode, 0); - o.maxViewMode = this._resolveViewName(o.maxViewMode, 4); - - // Check that the start view is between min and max - o.startView = Math.min(o.startView, o.maxViewMode); - o.startView = Math.max(o.startView, o.minViewMode); - - // true, false, or Number > 0 - if (o.multidate !== true){ - o.multidate = Number(o.multidate) || false; - if (o.multidate !== false) - o.multidate = Math.max(0, o.multidate); - } - o.multidateSeparator = String(o.multidateSeparator); - - o.weekStart %= 7; - o.weekEnd = (o.weekStart + 6) % 7; - - var format = DPGlobal.parseFormat(o.format); - if (o.startDate !== -Infinity){ - if (!!o.startDate){ - if (o.startDate instanceof Date) - o.startDate = this._local_to_utc(this._zero_time(o.startDate)); - else - o.startDate = DPGlobal.parseDate(o.startDate, format, o.language, o.assumeNearbyYear); - } - else { - o.startDate = -Infinity; - } - } - if (o.endDate !== Infinity){ - if (!!o.endDate){ - if (o.endDate instanceof Date) - o.endDate = this._local_to_utc(this._zero_time(o.endDate)); - else - o.endDate = DPGlobal.parseDate(o.endDate, format, o.language, o.assumeNearbyYear); - } - else { - o.endDate = Infinity; - } - } - - o.daysOfWeekDisabled = o.daysOfWeekDisabled||[]; - if (!$.isArray(o.daysOfWeekDisabled)) - o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/); - o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){ - return parseInt(d, 10); - }); - - o.daysOfWeekHighlighted = o.daysOfWeekHighlighted||[]; - if (!$.isArray(o.daysOfWeekHighlighted)) - o.daysOfWeekHighlighted = o.daysOfWeekHighlighted.split(/[,\s]*/); - o.daysOfWeekHighlighted = $.map(o.daysOfWeekHighlighted, function(d){ - return parseInt(d, 10); - }); - - o.datesDisabled = o.datesDisabled||[]; - if (!$.isArray(o.datesDisabled)) { - o.datesDisabled = [ - o.datesDisabled - ]; - } - o.datesDisabled = $.map(o.datesDisabled,function(d){ - return DPGlobal.parseDate(d, format, o.language, o.assumeNearbyYear); - }); - - var plc = String(o.orientation).toLowerCase().split(/\s+/g), - _plc = o.orientation.toLowerCase(); - plc = $.grep(plc, function(word){ - return /^auto|left|right|top|bottom$/.test(word); - }); - o.orientation = {x: 'auto', y: 'auto'}; - if (!_plc || _plc === 'auto') - ; // no action - else if (plc.length === 1){ - switch (plc[0]){ - case 'top': - case 'bottom': - o.orientation.y = plc[0]; - break; - case 'left': - case 'right': - o.orientation.x = plc[0]; - break; - } - } - else { - _plc = $.grep(plc, function(word){ - return /^left|right$/.test(word); - }); - o.orientation.x = _plc[0] || 'auto'; - - _plc = $.grep(plc, function(word){ - return /^top|bottom$/.test(word); - }); - o.orientation.y = _plc[0] || 'auto'; - } - if (o.defaultViewDate) { - var year = o.defaultViewDate.year || new Date().getFullYear(); - var month = o.defaultViewDate.month || 0; - var day = o.defaultViewDate.day || 1; - o.defaultViewDate = UTCDate(year, month, day); - } else { - o.defaultViewDate = UTCToday(); - } - }, - _events: [], - _secondaryEvents: [], - _applyEvents: function(evs){ - for (var i=0, el, ch, ev; i < evs.length; i++){ - el = evs[i][0]; - if (evs[i].length === 2){ - ch = undefined; - ev = evs[i][1]; - } - else if (evs[i].length === 3){ - ch = evs[i][1]; - ev = evs[i][2]; - } - el.on(ev, ch); - } - }, - _unapplyEvents: function(evs){ - for (var i=0, el, ev, ch; i < evs.length; i++){ - el = evs[i][0]; - if (evs[i].length === 2){ - ch = undefined; - ev = evs[i][1]; - } - else if (evs[i].length === 3){ - ch = evs[i][1]; - ev = evs[i][2]; - } - el.off(ev, ch); - } - }, - _buildEvents: function(){ - var events = { - keyup: $.proxy(function(e){ - if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1) - this.update(); - }, this), - keydown: $.proxy(this.keydown, this), - paste: $.proxy(this.paste, this) - }; - - if (this.o.showOnFocus === true) { - events.focus = $.proxy(this.show, this); - } - - if (this.isInput) { // single input - this._events = [ - [this.element, events] - ]; - } - else if (this.component && this.hasInput) { // component: input + button - this._events = [ - // For components that are not readonly, allow keyboard nav - [this.inputField, events], - [this.component, { - click: $.proxy(this.show, this) - }] - ]; - } - else { - this._events = [ - [this.element, { - click: $.proxy(this.show, this), - keydown: $.proxy(this.keydown, this) - }] - ]; - } - this._events.push( - // Component: listen for blur on element descendants - [this.element, '*', { - blur: $.proxy(function(e){ - this._focused_from = e.target; - }, this) - }], - // Input: listen for blur on element - [this.element, { - blur: $.proxy(function(e){ - this._focused_from = e.target; - }, this) - }] - ); - - if (this.o.immediateUpdates) { - // Trigger input updates immediately on changed year/month - this._events.push([this.element, { - 'changeYear changeMonth': $.proxy(function(e){ - this.update(e.date); - }, this) - }]); - } - - this._secondaryEvents = [ - [this.picker, { - click: $.proxy(this.click, this) - }], - [$(window), { - resize: $.proxy(this.place, this) - }], - [$(document), { - mousedown: $.proxy(function(e){ - // Clicked outside the datepicker, hide it - if (!( - this.element.is(e.target) || - this.element.find(e.target).length || - this.picker.is(e.target) || - this.picker.find(e.target).length || - this.isInline - )){ - this.hide(); - } - }, this) - }] - ]; - }, - _attachEvents: function(){ - this._detachEvents(); - this._applyEvents(this._events); - }, - _detachEvents: function(){ - this._unapplyEvents(this._events); - }, - _attachSecondaryEvents: function(){ - this._detachSecondaryEvents(); - this._applyEvents(this._secondaryEvents); - }, - _detachSecondaryEvents: function(){ - this._unapplyEvents(this._secondaryEvents); - }, - _trigger: function(event, altdate){ - var date = altdate || this.dates.get(-1), - local_date = this._utc_to_local(date); - - this.element.trigger({ - type: event, - date: local_date, - dates: $.map(this.dates, this._utc_to_local), - format: $.proxy(function(ix, format){ - if (arguments.length === 0){ - ix = this.dates.length - 1; - format = this.o.format; - } - else if (typeof ix === 'string'){ - format = ix; - ix = this.dates.length - 1; - } - format = format || this.o.format; - var date = this.dates.get(ix); - return DPGlobal.formatDate(date, format, this.o.language); - }, this) - }); - }, - - show: function(){ - if (this.inputField.prop('disabled') || (this.inputField.prop('readonly') && this.o.enableOnReadonly === false)) - return; - if (!this.isInline) - this.picker.appendTo(this.o.container); - this.place(); - this.picker.show(); - this._attachSecondaryEvents(); - this._trigger('show'); - if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) { - $(this.element).blur(); - } - return this; - }, - - hide: function(){ - if (this.isInline || !this.picker.is(':visible')) - return this; - this.focusDate = null; - this.picker.hide().detach(); - this._detachSecondaryEvents(); - this.viewMode = this.o.startView; - this.showMode(); - - if (this.o.forceParse && this.inputField.val()) - this.setValue(); - this._trigger('hide'); - return this; - }, - - destroy: function(){ - this.hide(); - this._detachEvents(); - this._detachSecondaryEvents(); - this.picker.remove(); - delete this.element.data().datepicker; - if (!this.isInput){ - delete this.element.data().date; - } - return this; - }, - - paste: function(evt){ - var dateString; - if (evt.originalEvent.clipboardData && evt.originalEvent.clipboardData.types - && $.inArray('text/plain', evt.originalEvent.clipboardData.types) !== -1) { - dateString = evt.originalEvent.clipboardData.getData('text/plain'); - } - else if (window.clipboardData) { - dateString = window.clipboardData.getData('Text'); - } - else { - return; - } - this.setDate(dateString); - this.update(); - evt.preventDefault(); - }, - - _utc_to_local: function(utc){ - return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000)); - }, - _local_to_utc: function(local){ - return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000)); - }, - _zero_time: function(local){ - return local && new Date(local.getFullYear(), local.getMonth(), local.getDate()); - }, - _zero_utc_time: function(utc){ - return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate())); - }, - - getDates: function(){ - return $.map(this.dates, this._utc_to_local); - }, - - getUTCDates: function(){ - return $.map(this.dates, function(d){ - return new Date(d); - }); - }, - - getDate: function(){ - return this._utc_to_local(this.getUTCDate()); - }, - - getUTCDate: function(){ - var selected_date = this.dates.get(-1); - if (typeof selected_date !== 'undefined') { - return new Date(selected_date); - } else { - return null; - } - }, - - clearDates: function(){ - if (this.inputField) { - this.inputField.val(''); - } - - this.update(); - this._trigger('changeDate'); - - if (this.o.autoclose) { - this.hide(); - } - }, - setDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; - this.update.apply(this, args); - this._trigger('changeDate'); - this.setValue(); - return this; - }, - - setUTCDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; - this.update.apply(this, $.map(args, this._utc_to_local)); - this._trigger('changeDate'); - this.setValue(); - return this; - }, - - setDate: alias('setDates'), - setUTCDate: alias('setUTCDates'), - remove: alias('destroy'), - - setValue: function(){ - var formatted = this.getFormattedDate(); - this.inputField.val(formatted); - return this; - }, - - getFormattedDate: function(format){ - if (format === undefined) - format = this.o.format; - - var lang = this.o.language; - return $.map(this.dates, function(d){ - return DPGlobal.formatDate(d, format, lang); - }).join(this.o.multidateSeparator); - }, - - getStartDate: function(){ - return this.o.startDate; - }, - - setStartDate: function(startDate){ - this._process_options({startDate: startDate}); - this.update(); - this.updateNavArrows(); - return this; - }, - - getEndDate: function(){ - return this.o.endDate; - }, - - setEndDate: function(endDate){ - this._process_options({endDate: endDate}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setDaysOfWeekDisabled: function(daysOfWeekDisabled){ - this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setDaysOfWeekHighlighted: function(daysOfWeekHighlighted){ - this._process_options({daysOfWeekHighlighted: daysOfWeekHighlighted}); - this.update(); - return this; - }, - - setDatesDisabled: function(datesDisabled){ - this._process_options({datesDisabled: datesDisabled}); - this.update(); - this.updateNavArrows(); - }, - - place: function(){ - if (this.isInline) - return this; - var calendarWidth = this.picker.outerWidth(), - calendarHeight = this.picker.outerHeight(), - visualPadding = 10, - container = $(this.o.container), - windowWidth = container.width(), - scrollTop = this.o.container === 'body' ? $(document).scrollTop() : container.scrollTop(), - appendOffset = container.offset(); - - var parentsZindex = []; - this.element.parents().each(function(){ - var itemZIndex = $(this).css('z-index'); - if (itemZIndex !== 'auto' && itemZIndex !== 0) parentsZindex.push(parseInt(itemZIndex)); - }); - var zIndex = Math.max.apply(Math, parentsZindex) + this.o.zIndexOffset; - var offset = this.component ? this.component.parent().offset() : this.element.offset(); - var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false); - var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false); - var left = offset.left - appendOffset.left, - top = offset.top - appendOffset.top; - - if (this.o.container !== 'body') { - top += scrollTop; - } - - this.picker.removeClass( - 'datepicker-orient-top datepicker-orient-bottom '+ - 'datepicker-orient-right datepicker-orient-left' - ); - - if (this.o.orientation.x !== 'auto'){ - this.picker.addClass('datepicker-orient-' + this.o.orientation.x); - if (this.o.orientation.x === 'right') - left -= calendarWidth - width; - } - // auto x orientation is best-placement: if it crosses a window - // edge, fudge it sideways - else { - if (offset.left < 0) { - // component is outside the window on the left side. Move it into visible range - this.picker.addClass('datepicker-orient-left'); - left -= offset.left - visualPadding; - } else if (left + calendarWidth > windowWidth) { - // the calendar passes the widow right edge. Align it to component right side - this.picker.addClass('datepicker-orient-right'); - left += width - calendarWidth; - } else { - // Default to left - this.picker.addClass('datepicker-orient-left'); - } - } - - // auto y orientation is best-situation: top or bottom, no fudging, - // decision based on which shows more of the calendar - var yorient = this.o.orientation.y, - top_overflow; - if (yorient === 'auto'){ - top_overflow = -scrollTop + top - calendarHeight; - yorient = top_overflow < 0 ? 'bottom' : 'top'; - } - - this.picker.addClass('datepicker-orient-' + yorient); - if (yorient === 'top') - top -= calendarHeight + parseInt(this.picker.css('padding-top')); - else - top += height; - - if (this.o.rtl) { - var right = windowWidth - (left + width); - this.picker.css({ - top: top, - right: right, - zIndex: zIndex - }); - } else { - this.picker.css({ - top: top, - left: left, - zIndex: zIndex - }); - } - return this; - }, - - _allow_update: true, - update: function(){ - if (!this._allow_update) - return this; - - var oldDates = this.dates.copy(), - dates = [], - fromArgs = false; - if (arguments.length){ - $.each(arguments, $.proxy(function(i, date){ - if (date instanceof Date) - date = this._local_to_utc(date); - dates.push(date); - }, this)); - fromArgs = true; - } - else { - dates = this.isInput - ? this.element.val() - : this.element.data('date') || this.inputField.val(); - if (dates && this.o.multidate) - dates = dates.split(this.o.multidateSeparator); - else - dates = [dates]; - delete this.element.data().date; - } - - dates = $.map(dates, $.proxy(function(date){ - return DPGlobal.parseDate(date, this.o.format, this.o.language, this.o.assumeNearbyYear); - }, this)); - dates = $.grep(dates, $.proxy(function(date){ - return ( - !this.dateWithinRange(date) || - !date - ); - }, this), true); - this.dates.replace(dates); - - if (this.dates.length) - this.viewDate = new Date(this.dates.get(-1)); - else if (this.viewDate < this.o.startDate) - this.viewDate = new Date(this.o.startDate); - else if (this.viewDate > this.o.endDate) - this.viewDate = new Date(this.o.endDate); - else - this.viewDate = this.o.defaultViewDate; - - if (fromArgs){ - // setting date by clicking - this.setValue(); - } - else if (dates.length){ - // setting date by typing - if (String(oldDates) !== String(this.dates)) - this._trigger('changeDate'); - } - if (!this.dates.length && oldDates.length) - this._trigger('clearDate'); - - this.fill(); - this.element.change(); - return this; - }, - - fillDow: function(){ - var dowCnt = this.o.weekStart, - html = ''; - if (this.o.calendarWeeks){ - this.picker.find('.datepicker-days .datepicker-switch') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - html += ' '; - } - while (dowCnt < this.o.weekStart + 7){ - html += ''+dates[this.o.language].daysMin[(dowCnt++)%7]+''; - } - html += ''; - this.picker.find('.datepicker-days thead').append(html); - }, - - fillMonths: function(){ - var localDate = this._utc_to_local(this.viewDate); - var html = '', - i = 0; - while (i < 12){ - var focused = localDate && localDate.getMonth() === i ? ' focused' : ''; - html += '' + dates[this.o.language].monthsShort[i++]+''; - } - this.picker.find('.datepicker-months td').html(html); - }, - - setRange: function(range){ - if (!range || !range.length) - delete this.range; - else - this.range = $.map(range, function(d){ - return d.valueOf(); - }); - this.fill(); - }, - - getClassNames: function(date){ - var cls = [], - year = this.viewDate.getUTCFullYear(), - month = this.viewDate.getUTCMonth(), - today = new Date(); - if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){ - cls.push('old'); - } - else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){ - cls.push('new'); - } - if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) - cls.push('focused'); - // Compare internal UTC date with local today, not UTC today - if (this.o.todayHighlight && - date.getUTCFullYear() === today.getFullYear() && - date.getUTCMonth() === today.getMonth() && - date.getUTCDate() === today.getDate()){ - cls.push('today'); - } - if (this.dates.contains(date) !== -1) - cls.push('active'); - if (!this.dateWithinRange(date)){ - cls.push('disabled'); - } - if (this.dateIsDisabled(date)){ - cls.push('disabled', 'disabled-date'); - } - if ($.inArray(date.getUTCDay(), this.o.daysOfWeekHighlighted) !== -1){ - cls.push('highlighted'); - } - - if (this.range){ - if (date > this.range[0] && date < this.range[this.range.length-1]){ - cls.push('range'); - } - if ($.inArray(date.valueOf(), this.range) !== -1){ - cls.push('selected'); - } - if (date.valueOf() === this.range[0]){ - cls.push('range-start'); - } - if (date.valueOf() === this.range[this.range.length-1]){ - cls.push('range-end'); - } - } - return cls; - }, - - _fill_yearsView: function(selector, cssClass, factor, step, currentYear, startYear, endYear, callback){ - var html, view, year, steps, startStep, endStep, thisYear, i, classes, tooltip, before; - - html = ''; - view = this.picker.find(selector); - year = parseInt(currentYear / factor, 10) * factor; - startStep = parseInt(startYear / step, 10) * step; - endStep = parseInt(endYear / step, 10) * step; - steps = $.map(this.dates, function(d){ - return parseInt(d.getUTCFullYear() / step, 10) * step; - }); - - view.find('.datepicker-switch').text(year + '-' + (year + step * 9)); - - thisYear = year - step; - for (i = -1; i < 11; i += 1) { - classes = [cssClass]; - tooltip = null; - - if (i === -1) { - classes.push('old'); - } else if (i === 10) { - classes.push('new'); - } - if ($.inArray(thisYear, steps) !== -1) { - classes.push('active'); - } - if (thisYear < startStep || thisYear > endStep) { - classes.push('disabled'); - } - if (thisYear === this.viewDate.getFullYear()) { - classes.push('focused'); - } - - if (callback !== $.noop) { - before = callback(new Date(thisYear, 0, 1)); - if (before === undefined) { - before = {}; - } else if (typeof(before) === 'boolean') { - before = {enabled: before}; - } else if (typeof(before) === 'string') { - before = {classes: before}; - } - if (before.enabled === false) { - classes.push('disabled'); - } - if (before.classes) { - classes = classes.concat(before.classes.split(/\s+/)); - } - if (before.tooltip) { - tooltip = before.tooltip; - } - } - - html += '' + thisYear + ''; - thisYear += step; - } - view.find('td').html(html); - }, - - fill: function(){ - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(), - startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, - startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, - endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, - endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, - todaytxt = dates[this.o.language].today || dates['en'].today || '', - cleartxt = dates[this.o.language].clear || dates['en'].clear || '', - titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat, - tooltip, - before; - if (isNaN(year) || isNaN(month)) - return; - this.picker.find('.datepicker-days .datepicker-switch') - .text(DPGlobal.formatDate(d, titleFormat, this.o.language)); - this.picker.find('tfoot .today') - .text(todaytxt) - .toggle(this.o.todayBtn !== false); - this.picker.find('tfoot .clear') - .text(cleartxt) - .toggle(this.o.clearBtn !== false); - this.picker.find('thead .datepicker-title') - .text(this.o.title) - .toggle(this.o.title !== ''); - this.updateNavArrows(); - this.fillMonths(); - var prevMonth = UTCDate(year, month-1, 28), - day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); - prevMonth.setUTCDate(day); - prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); - var nextMonth = new Date(prevMonth); - if (prevMonth.getUTCFullYear() < 100){ - nextMonth.setUTCFullYear(prevMonth.getUTCFullYear()); - } - nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); - nextMonth = nextMonth.valueOf(); - var html = []; - var clsName; - while (prevMonth.valueOf() < nextMonth){ - if (prevMonth.getUTCDay() === this.o.weekStart){ - html.push(''); - if (this.o.calendarWeeks){ - // ISO 8601: First week contains first thursday. - // ISO also states week starts on Monday, but we can be more abstract here. - var - // Start of current week: based on weekstart/current date - ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), - // Thursday of this week - th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), - // First Thursday of year, year from thursday - yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), - // Calendar week: ms between thursdays, div ms per day, div 7 days - calWeek = (th - yth) / 864e5 / 7 + 1; - html.push(''+ calWeek +''); - } - } - clsName = this.getClassNames(prevMonth); - clsName.push('day'); - - if (this.o.beforeShowDay !== $.noop){ - before = this.o.beforeShowDay(this._utc_to_local(prevMonth)); - if (before === undefined) - before = {}; - else if (typeof(before) === 'boolean') - before = {enabled: before}; - else if (typeof(before) === 'string') - before = {classes: before}; - if (before.enabled === false) - clsName.push('disabled'); - if (before.classes) - clsName = clsName.concat(before.classes.split(/\s+/)); - if (before.tooltip) - tooltip = before.tooltip; - } - - clsName = $.unique(clsName); - html.push(''+prevMonth.getUTCDate() + ''); - tooltip = null; - if (prevMonth.getUTCDay() === this.o.weekEnd){ - html.push(''); - } - prevMonth.setUTCDate(prevMonth.getUTCDate()+1); - } - this.picker.find('.datepicker-days tbody').empty().append(html.join('')); - - var monthsTitle = dates[this.o.language].monthsTitle || dates['en'].monthsTitle || 'Months'; - var months = this.picker.find('.datepicker-months') - .find('.datepicker-switch') - .text(this.o.maxViewMode < 2 ? monthsTitle : year) - .end() - .find('span').removeClass('active'); - - $.each(this.dates, function(i, d){ - if (d.getUTCFullYear() === year) - months.eq(d.getUTCMonth()).addClass('active'); - }); - - if (year < startYear || year > endYear){ - months.addClass('disabled'); - } - if (year === startYear){ - months.slice(0, startMonth).addClass('disabled'); - } - if (year === endYear){ - months.slice(endMonth+1).addClass('disabled'); - } - - if (this.o.beforeShowMonth !== $.noop){ - var that = this; - $.each(months, function(i, month){ - var moDate = new Date(year, i, 1); - var before = that.o.beforeShowMonth(moDate); - if (before === undefined) - before = {}; - else if (typeof(before) === 'boolean') - before = {enabled: before}; - else if (typeof(before) === 'string') - before = {classes: before}; - if (before.enabled === false && !$(month).hasClass('disabled')) - $(month).addClass('disabled'); - if (before.classes) - $(month).addClass(before.classes); - if (before.tooltip) - $(month).prop('title', before.tooltip); - }); - } - - // Generating decade/years picker - this._fill_yearsView( - '.datepicker-years', - 'year', - 10, - 1, - year, - startYear, - endYear, - this.o.beforeShowYear - ); - - // Generating century/decades picker - this._fill_yearsView( - '.datepicker-decades', - 'decade', - 100, - 10, - year, - startYear, - endYear, - this.o.beforeShowDecade - ); - - // Generating millennium/centuries picker - this._fill_yearsView( - '.datepicker-centuries', - 'century', - 1000, - 100, - year, - startYear, - endYear, - this.o.beforeShowCentury - ); - }, - - updateNavArrows: function(){ - if (!this._allow_update) - return; - - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(); - switch (this.viewMode){ - case 0: - if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){ - this.picker.find('.prev').css({visibility: 'hidden'}); - } - else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){ - this.picker.find('.next').css({visibility: 'hidden'}); - } - else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - case 1: - case 2: - case 3: - case 4: - if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() || this.o.maxViewMode < 2){ - this.picker.find('.prev').css({visibility: 'hidden'}); - } - else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() || this.o.maxViewMode < 2){ - this.picker.find('.next').css({visibility: 'hidden'}); - } - else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - } - }, - - click: function(e){ - e.preventDefault(); - e.stopPropagation(); - - var target, dir, day, year, month, monthChanged, yearChanged; - target = $(e.target); - - // Clicked on the switch - if (target.hasClass('datepicker-switch')){ - this.showMode(1); - } - - // Clicked on prev or next - var navArrow = target.closest('.prev, .next'); - if (navArrow.length > 0) { - dir = DPGlobal.modes[this.viewMode].navStep * (navArrow.hasClass('prev') ? -1 : 1); - if (this.viewMode === 0){ - this.viewDate = this.moveMonth(this.viewDate, dir); - this._trigger('changeMonth', this.viewDate); - } else { - this.viewDate = this.moveYear(this.viewDate, dir); - if (this.viewMode === 1){ - this._trigger('changeYear', this.viewDate); - } - } - this.fill(); - } - - // Clicked on today button - if (target.hasClass('today') && !target.hasClass('day')){ - this.showMode(-2); - this._setDate(UTCToday(), this.o.todayBtn === 'linked' ? null : 'view'); - } - - // Clicked on clear button - if (target.hasClass('clear')){ - this.clearDates(); - } - - if (!target.hasClass('disabled')){ - // Clicked on a day - if (target.hasClass('day')){ - day = parseInt(target.text(), 10) || 1; - year = this.viewDate.getUTCFullYear(); - month = this.viewDate.getUTCMonth(); - - // From last month - if (target.hasClass('old')){ - if (month === 0) { - month = 11; - year = year - 1; - monthChanged = true; - yearChanged = true; - } else { - month = month - 1; - monthChanged = true; - } - } - - // From next month - if (target.hasClass('new')) { - if (month === 11){ - month = 0; - year = year + 1; - monthChanged = true; - yearChanged = true; - } else { - month = month + 1; - monthChanged = true; - } - } - this._setDate(UTCDate(year, month, day)); - if (yearChanged) { - this._trigger('changeYear', this.viewDate); - } - if (monthChanged) { - this._trigger('changeMonth', this.viewDate); - } - } - - // Clicked on a month - if (target.hasClass('month')) { - this.viewDate.setUTCDate(1); - day = 1; - month = target.parent().find('span').index(target); - year = this.viewDate.getUTCFullYear(); - this.viewDate.setUTCMonth(month); - this._trigger('changeMonth', this.viewDate); - if (this.o.minViewMode === 1){ - this._setDate(UTCDate(year, month, day)); - this.showMode(); - } else { - this.showMode(-1); - } - this.fill(); - } - - // Clicked on a year - if (target.hasClass('year') - || target.hasClass('decade') - || target.hasClass('century')) { - this.viewDate.setUTCDate(1); - - day = 1; - month = 0; - year = parseInt(target.text(), 10)||0; - this.viewDate.setUTCFullYear(year); - - if (target.hasClass('year')){ - this._trigger('changeYear', this.viewDate); - if (this.o.minViewMode === 2){ - this._setDate(UTCDate(year, month, day)); - } - } - if (target.hasClass('decade')){ - this._trigger('changeDecade', this.viewDate); - if (this.o.minViewMode === 3){ - this._setDate(UTCDate(year, month, day)); - } - } - if (target.hasClass('century')){ - this._trigger('changeCentury', this.viewDate); - if (this.o.minViewMode === 4){ - this._setDate(UTCDate(year, month, day)); - } - } - - this.showMode(-1); - this.fill(); - } - } - - if (this.picker.is(':visible') && this._focused_from){ - $(this._focused_from).focus(); - } - delete this._focused_from; - }, - - _toggle_multidate: function(date){ - var ix = this.dates.contains(date); - if (!date){ - this.dates.clear(); - } - - if (ix !== -1){ - if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){ - this.dates.remove(ix); - } - } else if (this.o.multidate === false) { - this.dates.clear(); - this.dates.push(date); - } - else { - this.dates.push(date); - } - - if (typeof this.o.multidate === 'number') - while (this.dates.length > this.o.multidate) - this.dates.remove(0); - }, - - _setDate: function(date, which){ - if (!which || which === 'date') - this._toggle_multidate(date && new Date(date)); - if (!which || which === 'view') - this.viewDate = date && new Date(date); - - this.fill(); - this.setValue(); - if (!which || which !== 'view') { - this._trigger('changeDate'); - } - if (this.inputField){ - this.inputField.change(); - } - if (this.o.autoclose && (!which || which === 'date')){ - this.hide(); - } - }, - - moveDay: function(date, dir){ - var newDate = new Date(date); - newDate.setUTCDate(date.getUTCDate() + dir); - - return newDate; - }, - - moveWeek: function(date, dir){ - return this.moveDay(date, dir * 7); - }, - - moveMonth: function(date, dir){ - if (!isValidDate(date)) - return this.o.defaultViewDate; - if (!dir) - return date; - var new_date = new Date(date.valueOf()), - day = new_date.getUTCDate(), - month = new_date.getUTCMonth(), - mag = Math.abs(dir), - new_month, test; - dir = dir > 0 ? 1 : -1; - if (mag === 1){ - test = dir === -1 - // If going back one month, make sure month is not current month - // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) - ? function(){ - return new_date.getUTCMonth() === month; - } - // If going forward one month, make sure month is as expected - // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) - : function(){ - return new_date.getUTCMonth() !== new_month; - }; - new_month = month + dir; - new_date.setUTCMonth(new_month); - // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 - if (new_month < 0 || new_month > 11) - new_month = (new_month + 12) % 12; - } - else { - // For magnitudes >1, move one month at a time... - for (var i=0; i < mag; i++) - // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... - new_date = this.moveMonth(new_date, dir); - // ...then reset the day, keeping it in the new month - new_month = new_date.getUTCMonth(); - new_date.setUTCDate(day); - test = function(){ - return new_month !== new_date.getUTCMonth(); - }; - } - // Common date-resetting loop -- if date is beyond end of month, make it - // end of month - while (test()){ - new_date.setUTCDate(--day); - new_date.setUTCMonth(new_month); - } - return new_date; - }, - - moveYear: function(date, dir){ - return this.moveMonth(date, dir*12); - }, - - moveAvailableDate: function(date, dir, fn){ - do { - date = this[fn](date, dir); - - if (!this.dateWithinRange(date)) - return false; - - fn = 'moveDay'; - } - while (this.dateIsDisabled(date)); - - return date; - }, - - weekOfDateIsDisabled: function(date){ - return $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1; - }, - - dateIsDisabled: function(date){ - return ( - this.weekOfDateIsDisabled(date) || - $.grep(this.o.datesDisabled, function(d){ - return isUTCEquals(date, d); - }).length > 0 - ); - }, - - dateWithinRange: function(date){ - return date >= this.o.startDate && date <= this.o.endDate; - }, - - keydown: function(e){ - if (!this.picker.is(':visible')){ - if (e.keyCode === 40 || e.keyCode === 27) { // allow down to re-show picker - this.show(); - e.stopPropagation(); - } - return; - } - var dateChanged = false, - dir, newViewDate, - focusDate = this.focusDate || this.viewDate; - switch (e.keyCode){ - case 27: // escape - if (this.focusDate){ - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.fill(); - } - else - this.hide(); - e.preventDefault(); - e.stopPropagation(); - break; - case 37: // left - case 38: // up - case 39: // right - case 40: // down - if (!this.o.keyboardNavigation || this.o.daysOfWeekDisabled.length === 7) - break; - dir = e.keyCode === 37 || e.keyCode === 38 ? -1 : 1; - if (this.viewMode === 0) { - if (e.ctrlKey){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear'); - - if (newViewDate) - this._trigger('changeYear', this.viewDate); - } - else if (e.shiftKey){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth'); - - if (newViewDate) - this._trigger('changeMonth', this.viewDate); - } - else if (e.keyCode === 37 || e.keyCode === 39){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveDay'); - } - else if (!this.weekOfDateIsDisabled(focusDate)){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveWeek'); - } - } else if (this.viewMode === 1) { - if (e.keyCode === 38 || e.keyCode === 40) { - dir = dir * 4; - } - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth'); - } else if (this.viewMode === 2) { - if (e.keyCode === 38 || e.keyCode === 40) { - dir = dir * 4; - } - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear'); - } - if (newViewDate){ - this.focusDate = this.viewDate = newViewDate; - this.setValue(); - this.fill(); - e.preventDefault(); - } - break; - case 13: // enter - if (!this.o.forceParse) - break; - focusDate = this.focusDate || this.dates.get(-1) || this.viewDate; - if (this.o.keyboardNavigation) { - this._toggle_multidate(focusDate); - dateChanged = true; - } - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.setValue(); - this.fill(); - if (this.picker.is(':visible')){ - e.preventDefault(); - e.stopPropagation(); - if (this.o.autoclose) - this.hide(); - } - break; - case 9: // tab - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.fill(); - this.hide(); - break; - } - if (dateChanged){ - if (this.dates.length) - this._trigger('changeDate'); - else - this._trigger('clearDate'); - if (this.inputField){ - this.inputField.change(); - } - } - }, - - showMode: function(dir){ - if (dir){ - this.viewMode = Math.max(this.o.minViewMode, Math.min(this.o.maxViewMode, this.viewMode + dir)); - } - this.picker - .children('div') - .hide() - .filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName) - .show(); - this.updateNavArrows(); - } - }; - - var DateRangePicker = function(element, options){ - $(element).data('datepicker', this); - this.element = $(element); - this.inputs = $.map(options.inputs, function(i){ - return i.jquery ? i[0] : i; - }); - delete options.inputs; - - datepickerPlugin.call($(this.inputs), options) - .on('changeDate', $.proxy(this.dateUpdated, this)); - - this.pickers = $.map(this.inputs, function(i){ - return $(i).data('datepicker'); - }); - this.updateDates(); - }; - DateRangePicker.prototype = { - updateDates: function(){ - this.dates = $.map(this.pickers, function(i){ - return i.getUTCDate(); - }); - this.updateRanges(); - }, - updateRanges: function(){ - var range = $.map(this.dates, function(d){ - return d.valueOf(); - }); - $.each(this.pickers, function(i, p){ - p.setRange(range); - }); - }, - dateUpdated: function(e){ - // `this.updating` is a workaround for preventing infinite recursion - // between `changeDate` triggering and `setUTCDate` calling. Until - // there is a better mechanism. - if (this.updating) - return; - this.updating = true; - - var dp = $(e.target).data('datepicker'); - - if (typeof(dp) === "undefined") { - return; - } - - var new_date = dp.getUTCDate(), - i = $.inArray(e.target, this.inputs), - j = i - 1, - k = i + 1, - l = this.inputs.length; - if (i === -1) - return; - - $.each(this.pickers, function(i, p){ - if (!p.getUTCDate()) - p.setUTCDate(new_date); - }); - - if (new_date < this.dates[j]){ - // Date being moved earlier/left - while (j >= 0 && new_date < this.dates[j]){ - this.pickers[j--].setUTCDate(new_date); - } - } - else if (new_date > this.dates[k]){ - // Date being moved later/right - while (k < l && new_date > this.dates[k]){ - this.pickers[k++].setUTCDate(new_date); - } - } - this.updateDates(); - - delete this.updating; - }, - remove: function(){ - $.map(this.pickers, function(p){ p.remove(); }); - delete this.element.data().datepicker; - } - }; - - function opts_from_el(el, prefix){ - // Derive options from element data-attrs - var data = $(el).data(), - out = {}, inkey, - replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'); - prefix = new RegExp('^' + prefix.toLowerCase()); - function re_lower(_,a){ - return a.toLowerCase(); - } - for (var key in data) - if (prefix.test(key)){ - inkey = key.replace(replace, re_lower); - out[inkey] = data[key]; - } - return out; - } - - function opts_from_locale(lang){ - // Derive options from locale plugins - var out = {}; - // Check if "de-DE" style date is available, if not language should - // fallback to 2 letter code eg "de" - if (!dates[lang]){ - lang = lang.split('-')[0]; - if (!dates[lang]) - return; - } - var d = dates[lang]; - $.each(locale_opts, function(i,k){ - if (k in d) - out[k] = d[k]; - }); - return out; - } - - var old = $.fn.datepicker; - var datepickerPlugin = function(option){ - var args = Array.apply(null, arguments); - args.shift(); - var internal_return; - this.each(function(){ - var $this = $(this), - data = $this.data('datepicker'), - options = typeof option === 'object' && option; - if (!data){ - var elopts = opts_from_el(this, 'date'), - // Preliminary otions - xopts = $.extend({}, defaults, elopts, options), - locopts = opts_from_locale(xopts.language), - // Options priority: js args, data-attrs, locales, defaults - opts = $.extend({}, defaults, locopts, elopts, options); - if ($this.hasClass('input-daterange') || opts.inputs){ - $.extend(opts, { - inputs: opts.inputs || $this.find('input').toArray() - }); - data = new DateRangePicker(this, opts); - } - else { - data = new Datepicker(this, opts); - } - $this.data('datepicker', data); - } - if (typeof option === 'string' && typeof data[option] === 'function'){ - internal_return = data[option].apply(data, args); - } - }); - - if ( - internal_return === undefined || - internal_return instanceof Datepicker || - internal_return instanceof DateRangePicker - ) - return this; - - if (this.length > 1) - throw new Error('Using only allowed for the collection of a single element (' + option + ' function)'); - else - return internal_return; - }; - $.fn.datepicker = datepickerPlugin; - - var defaults = $.fn.datepicker.defaults = { - assumeNearbyYear: false, - autoclose: false, - beforeShowDay: $.noop, - beforeShowMonth: $.noop, - beforeShowYear: $.noop, - beforeShowDecade: $.noop, - beforeShowCentury: $.noop, - calendarWeeks: false, - clearBtn: false, - toggleActive: false, - daysOfWeekDisabled: [], - daysOfWeekHighlighted: [], - datesDisabled: [], - endDate: Infinity, - forceParse: true, - format: 'mm/dd/yyyy', - keyboardNavigation: true, - language: 'en', - minViewMode: 0, - maxViewMode: 4, - multidate: false, - multidateSeparator: ',', - orientation: "auto", - rtl: false, - startDate: -Infinity, - startView: 0, - todayBtn: false, - todayHighlight: false, - weekStart: 0, - disableTouchKeyboard: false, - enableOnReadonly: true, - showOnFocus: true, - zIndexOffset: 10, - container: 'body', - immediateUpdates: false, - title: '', - templates: { - leftArrow: '«', - rightArrow: '»' - } - }; - var locale_opts = $.fn.datepicker.locale_opts = [ - 'format', - 'rtl', - 'weekStart' - ]; - $.fn.datepicker.Constructor = Datepicker; - var dates = $.fn.datepicker.dates = { - en: { - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - today: "Today", - clear: "Clear", - titleFormat: "MM yyyy" - } - }; - - var DPGlobal = { - modes: [ - { - clsName: 'days', - navFnc: 'Month', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'FullYear', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'FullYear', - navStep: 10 - }, - { - clsName: 'decades', - navFnc: 'FullDecade', - navStep: 100 - }, - { - clsName: 'centuries', - navFnc: 'FullCentury', - navStep: 1000 - }], - isLeapYear: function(year){ - return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); - }, - getDaysInMonth: function(year, month){ - return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; - }, - validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, - nonpunctuation: /[^ -\/:-@\u5e74\u6708\u65e5\[-`{-~\t\n\r]+/g, - parseFormat: function(format){ - if (typeof format.toValue === 'function' && typeof format.toDisplay === 'function') - return format; - // IE treats \0 as a string end in inputs (truncating the value), - // so it's a bad format delimiter, anyway - var separators = format.replace(this.validParts, '\0').split('\0'), - parts = format.match(this.validParts); - if (!separators || !separators.length || !parts || parts.length === 0){ - throw new Error("Invalid date format."); - } - return {separators: separators, parts: parts}; - }, - parseDate: function(date, format, language, assumeNearby){ - if (!date) - return undefined; - if (date instanceof Date) - return date; - if (typeof format === 'string') - format = DPGlobal.parseFormat(format); - if (format.toValue) - return format.toValue(date, format, language); - var part_re = /([\-+]\d+)([dmwy])/, - parts = date.match(/([\-+]\d+)([dmwy])/g), - fn_map = { - d: 'moveDay', - m: 'moveMonth', - w: 'moveWeek', - y: 'moveYear' - }, - dateAliases = { - yesterday: '-1d', - today: '+0d', - tomorrow: '+1d' - }, - part, dir, i, fn; - if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){ - date = new Date(); - for (i=0; i < parts.length; i++){ - part = part_re.exec(parts[i]); - dir = parseInt(part[1]); - fn = fn_map[part[2]]; - date = Datepicker.prototype[fn](date, dir); - } - return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()); - } - - if (typeof dateAliases[date] !== 'undefined') { - date = dateAliases[date]; - parts = date.match(/([\-+]\d+)([dmwy])/g); - - if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){ - date = new Date(); - for (i=0; i < parts.length; i++){ - part = part_re.exec(parts[i]); - dir = parseInt(part[1]); - fn = fn_map[part[2]]; - date = Datepicker.prototype[fn](date, dir); - } - - return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()); - } - } - - parts = date && date.match(this.nonpunctuation) || []; - date = new Date(); - - function applyNearbyYear(year, threshold){ - if (threshold === true) - threshold = 10; - - // if year is 2 digits or less, than the user most likely is trying to get a recent century - if (year < 100){ - year += 2000; - // if the new year is more than threshold years in advance, use last century - if (year > ((new Date()).getFullYear()+threshold)){ - year -= 100; - } - } - - return year; - } - - var parsed = {}, - setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], - setters_map = { - yyyy: function(d,v){ - return d.setUTCFullYear(assumeNearby ? applyNearbyYear(v, assumeNearby) : v); - }, - yy: function(d,v){ - return d.setUTCFullYear(assumeNearby ? applyNearbyYear(v, assumeNearby) : v); - }, - m: function(d,v){ - if (isNaN(d)) - return d; - v -= 1; - while (v < 0) v += 12; - v %= 12; - d.setUTCMonth(v); - while (d.getUTCMonth() !== v) - d.setUTCDate(d.getUTCDate()-1); - return d; - }, - d: function(d,v){ - return d.setUTCDate(v); - } - }, - val, filtered; - setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; - setters_map['dd'] = setters_map['d']; - date = UTCToday(); - var fparts = format.parts.slice(); - // Remove noop parts - if (parts.length !== fparts.length){ - fparts = $(fparts).filter(function(i,p){ - return $.inArray(p, setters_order) !== -1; - }).toArray(); - } - // Process remainder - function match_part(){ - var m = this.slice(0, parts[i].length), - p = parts[i].slice(0, m.length); - return m.toLowerCase() === p.toLowerCase(); - } - if (parts.length === fparts.length){ - var cnt; - for (i=0, cnt = fparts.length; i < cnt; i++){ - val = parseInt(parts[i], 10); - part = fparts[i]; - if (isNaN(val)){ - switch (part){ - case 'MM': - filtered = $(dates[language].months).filter(match_part); - val = $.inArray(filtered[0], dates[language].months) + 1; - break; - case 'M': - filtered = $(dates[language].monthsShort).filter(match_part); - val = $.inArray(filtered[0], dates[language].monthsShort) + 1; - break; - } - } - parsed[part] = val; - } - var _date, s; - for (i=0; i < setters_order.length; i++){ - s = setters_order[i]; - if (s in parsed && !isNaN(parsed[s])){ - _date = new Date(date); - setters_map[s](_date, parsed[s]); - if (!isNaN(_date)) - date = _date; - } - } - } - return date; - }, - formatDate: function(date, format, language){ - if (!date) - return ''; - if (typeof format === 'string') - format = DPGlobal.parseFormat(format); - if (format.toDisplay) - return format.toDisplay(date, format, language); - var val = { - d: date.getUTCDate(), - D: dates[language].daysShort[date.getUTCDay()], - DD: dates[language].days[date.getUTCDay()], - m: date.getUTCMonth() + 1, - M: dates[language].monthsShort[date.getUTCMonth()], - MM: dates[language].months[date.getUTCMonth()], - yy: date.getUTCFullYear().toString().substring(2), - yyyy: date.getUTCFullYear() - }; - val.dd = (val.d < 10 ? '0' : '') + val.d; - val.mm = (val.m < 10 ? '0' : '') + val.m; - date = []; - var seps = $.extend([], format.separators); - for (var i=0, cnt = format.parts.length; i <= cnt; i++){ - if (seps.length) - date.push(seps.shift()); - date.push(val[format.parts[i]]); - } - return date.join(''); - }, - headTemplate: ''+ - ''+ - ''+ - ''+ - ''+ - '«'+ - ''+ - '»'+ - ''+ - '', - contTemplate: '', - footTemplate: ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - '' - }; - DPGlobal.template = '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - ''+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '; - - $.fn.datepicker.DPGlobal = DPGlobal; - - - /* DATEPICKER NO CONFLICT - * =================== */ - - $.fn.datepicker.noConflict = function(){ - $.fn.datepicker = old; - return this; - }; - - /* DATEPICKER VERSION - * =================== */ - $.fn.datepicker.version = '1.6.1'; - - /* DATEPICKER DATA-API - * ================== */ - - $(document).on( - 'focus.datepicker.data-api click.datepicker.data-api', - '[data-provide="datepicker"]', - function(e){ - var $this = $(this); - if ($this.data('datepicker')) - return; - e.preventDefault(); - // component click requires us to explicitly show it - datepickerPlugin.call($this, 'show'); - } - ); - $(function(){ - datepickerPlugin.call($('[data-provide="datepicker-inline"]')); - }); - -})); diff --git a/vendor/assets/javascripts/bootstrap-tagsinput.js b/vendor/assets/javascripts/bootstrap-tagsinput.js deleted file mode 100644 index 823256b..0000000 --- a/vendor/assets/javascripts/bootstrap-tagsinput.js +++ /dev/null @@ -1,682 +0,0 @@ -/* - * bootstrap-tagsinput v0.8.0 - * - */ - -(function ($) { - "use strict"; - - var defaultOptions = { - tagClass: function(item) { - return 'label label-info'; - }, - focusClass: 'focus', - itemValue: function(item) { - return item ? item.toString() : item; - }, - itemText: function(item) { - return this.itemValue(item); - }, - itemTitle: function(item) { - return null; - }, - freeInput: true, - addOnBlur: true, - maxTags: undefined, - maxChars: undefined, - confirmKeys: [13, 44], - delimiter: ',', - delimiterRegex: null, - cancelConfirmKeysOnEmpty: false, - onTagExists: function(item, $tag) { - $tag.hide().fadeIn(); - }, - trimValue: false, - allowDuplicates: false, - triggerChange: true - }; - - /** - * Constructor function - */ - function TagsInput(element, options) { - this.isInit = true; - this.itemsArray = []; - - this.$element = $(element); - this.$element.hide(); - - this.isSelect = (element.tagName === 'SELECT'); - this.multiple = (this.isSelect && element.hasAttribute('multiple')); - this.objectItems = options && options.itemValue; - this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : ''; - this.inputSize = Math.max(1, this.placeholderText.length); - - this.$container = $('
    '); - this.$input = $('').appendTo(this.$container); - - this.$element.before(this.$container); - - this.build(options); - this.isInit = false; - } - - TagsInput.prototype = { - constructor: TagsInput, - - /** - * Adds the given item as a new tag. Pass true to dontPushVal to prevent - * updating the elements val() - */ - add: function(item, dontPushVal, options) { - var self = this; - - if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags) - return; - - // Ignore falsey values, except false - if (item !== false && !item) - return; - - // Trim value - if (typeof item === "string" && self.options.trimValue) { - item = $.trim(item); - } - - // Throw an error when trying to add an object while the itemValue option was not set - if (typeof item === "object" && !self.objectItems) - throw("Can't add objects when itemValue option is not set"); - - // Ignore strings only containg whitespace - if (item.toString().match(/^\s*$/)) - return; - - // If SELECT but not multiple, remove current tag - if (self.isSelect && !self.multiple && self.itemsArray.length > 0) - self.remove(self.itemsArray[0]); - - if (typeof item === "string" && this.$element[0].tagName === 'INPUT') { - var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter; - var items = item.split(delimiter); - if (items.length > 1) { - for (var i = 0; i < items.length; i++) { - this.add(items[i], true); - } - - if (!dontPushVal) - self.pushVal(self.options.triggerChange); - return; - } - } - - var itemValue = self.options.itemValue(item), - itemText = self.options.itemText(item), - tagClass = self.options.tagClass(item), - itemTitle = self.options.itemTitle(item); - - // Ignore items allready added - var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0]; - if (existing && !self.options.allowDuplicates) { - // Invoke onTagExists - if (self.options.onTagExists) { - var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; }); - self.options.onTagExists(item, $existingTag); - } - return; - } - - // if length greater than limit - if (self.items().toString().length + item.length + 1 > self.options.maxInputLength) - return; - - // raise beforeItemAdd arg - var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options}); - self.$element.trigger(beforeItemAddEvent); - if (beforeItemAddEvent.cancel) - return; - - // register item in internal array and map - self.itemsArray.push(item); - - // add a tag element - - var $tag = $('' + htmlEncode(itemText) + ''); - $tag.data('item', item); - self.findInputWrapper().before($tag); - $tag.after(' '); - - // Check to see if the tag exists in its raw or uri-encoded form - var optionExists = ( - $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length || - $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length - ); - - // add