diff --git a/.gitignore b/.gitignore
index fc2af12..cce4b24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,5 @@ spec/dummy/tmp/
spec/dummy/.sass-cache
coverage
gemfiles/*.gemfile.lock
-Gemfile.lock
.yardoc
-doc
+doc
\ No newline at end of file
diff --git a/.rspec b/.rspec
deleted file mode 100644
index 4e1e0d2..0000000
--- a/.rspec
+++ /dev/null
@@ -1 +0,0 @@
---color
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7f7a8c4..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-language: ruby
-rvm:
- - 2.1
- - 2.2
- - 2.3
- - 2.4.1
-
-before_script:
- - cd spec/dummy
- - bundle exec rake db:migrate db:test:prepare
- - cd ../../
-
-gemfile:
- - gemfiles/3.2.gemfile
- - gemfiles/4.0.gemfile
- - gemfiles/4.1.gemfile
- - gemfiles/4.2.gemfile
- - gemfiles/5.0.gemfile
-
-matrix:
- exclude:
- - rvm: 2.1
- gemfile: gemfiles/5.0.gemfile
- - rvm: 2.4.1
- gemfile: gemfiles/3.2.gemfile
- - rvm: 2.4.1
- gemfile: gemfiles/4.0.gemfile
- - rvm: 2.4.1
- gemfile: gemfiles/4.1.gemfile
- - rvm: 2.4.1
- gemfile: gemfiles/4.2.gemfile
diff --git a/.yardopts b/.yardopts
deleted file mode 100644
index 3630bd2..0000000
--- a/.yardopts
+++ /dev/null
@@ -1,3 +0,0 @@
---no-private
---protected
---markup markdown
diff --git a/Appraisals b/Appraisals
deleted file mode 100644
index 4dddcce..0000000
--- a/Appraisals
+++ /dev/null
@@ -1,24 +0,0 @@
-if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
- appraise '3.2' do
- gem 'rails', '~> 3.2.22'
- gem 'test-unit', '~> 3.0'
- gem 'rack-cache', '< 1.3'
- end
-
- appraise '4.0' do
- gem 'rails', '~> 4.0.13'
- gem 'test-unit', '~> 3.0'
- end
-
- appraise '4.1' do
- gem 'rails', '~> 4.1.13'
- end
-
- appraise '4.2' do
- gem 'rails', '~> 4.2.4'
- end
-else
- appraise '5.0' do
- gem 'rails', '~> 5.0.2'
- end
-end
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15f2110..b2c0b34 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -128,4 +128,4 @@ This is the last version that uses the
The changelog began with version 1.0.0 so any changes prior to that
can be seen by checking the tagged releases and reading git commit
-messages.
+messages.
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 53026dd..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Contributing
-
-We love pull requests. Here's a quick guide:
-
-1. Fork the repo.
-
-2. Create your feature branch (`git checkout -b my-new-feature`)
-
-3. Install Gems `bundle`
-
-4. Create and migrate the test database `bundle exec rake db:create db:migrate RAILS_ENV=test`
-
-5. Run the tests. We only take pull requests with passing tests, and it's great
-to know that you have a clean slate: `bundle exec rake`
-
-6. Add a test for your change. Only refactoring and documentation changes
-require no new tests. If you are adding functionality or fixing a bug, we need
-a test!
-
-7. Make the test pass.
-
-8. Update [CHANGELOG.md](https://github.com/Ink/filepicker-rails/blob/master/CHANGELOG.md) with a brief description of your changes under the `unreleased` heading.
-
-9. Commit your changes (`git commit -am 'Added some feature'`)
-
-10. Push to the branch (`git push origin my-new-feature`)
-
-11. Create new Pull Request
-
-At this point you're waiting on us. We like to at least give you feedback, if not just
-accept it, within a few days, depending on our internal priorities.
-
-Some things that will increase the chance that your pull request is accepted is to follow the practices described on [Ruby style guide](https://github.com/bbatsov/ruby-style-guide), [Rails style guide](https://github.com/bbatsov/rails-style-guide) and [Better Specs](http://betterspecs.org/).
-
diff --git a/Gemfile b/Gemfile
index afc429e..426cc79 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
-source "https://rubygems.org"
+source 'https://rubygems.org'
-# Declare your gem's dependencies in filepicker_rails.gemspec.
+# Declare your gem's dependencies in filestack_rails.gemspec.
# Bundler will treat runtime dependencies like base dependencies, and
# development dependencies will be added by default to the :development group.
gemspec
@@ -10,5 +10,7 @@ gemspec
# Git. Remember to move these dependencies to your gemspec before releasing
# your gem to rubygems.org.
-# To use debugger
-# gem 'debugger'
+# To use a debugger
+# gem 'byebug', group: [:development, :test]
+
+gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
diff --git a/MIT-LICENSE b/MIT-LICENSE
index 5146945..056b959 100644
--- a/MIT-LICENSE
+++ b/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright 2013 YOURNAME
+Copyright 2017 Richard
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
index a7d8802..e34066e 100644
--- a/README.md
+++ b/README.md
@@ -193,4 +193,4 @@ Thank you to all the [contributors](https://github.com/filestack/filepicker-rail
[code_climate]: https://codeclimate.com/github/filestack/filestack-rails
[code_climate_badge]: https://codeclimate.com/github/filestack/filestack-rails.png
[coveralls]: https://coveralls.io/github/filestack/filestack-rails?branch=master
-[coveralls_badge]: https://coveralls.io/repos/github/filestack/filestack-rails/badge.svg?branch=master
+[coveralls_badge]: https://coveralls.io/repos/github/filestack/filestack-rails/badge.svg?branch=master
\ No newline at end of file
diff --git a/Rakefile b/Rakefile
index 23beba3..115b5c4 100644
--- a/Rakefile
+++ b/Rakefile
@@ -12,7 +12,7 @@ RSpec::Core::RakeTask.new
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
- rdoc.title = 'FilepickerRails'
+ rdoc.title = 'FilestackRails'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
@@ -22,16 +22,7 @@ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
task :default do
- if ENV['BUNDLE_GEMFILE'] =~ /gemfiles/
- Rake::Task['spec'].invoke
- else
- Rake::Task['appraise'].invoke
- end
-end
-
-task :appraise do
- exec 'appraisal install && appraisal rake'
+ Rake::Task['spec'].invoke
end
Bundler::GemHelper.install_tasks
-
diff --git a/app/controllers/filestack_rails/application_controller.rb b/app/controllers/filestack_rails/application_controller.rb
new file mode 100644
index 0000000..545d0b7
--- /dev/null
+++ b/app/controllers/filestack_rails/application_controller.rb
@@ -0,0 +1,5 @@
+module FilestackRails
+ class ApplicationController < ActionController::Base
+ protect_from_forgery with: :exception
+ end
+end
diff --git a/app/helpers/filepicker_rails/application_helper.rb b/app/helpers/filepicker_rails/application_helper.rb
deleted file mode 100644
index c6d6577..0000000
--- a/app/helpers/filepicker_rails/application_helper.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-module FilepickerRails
- module ApplicationHelper
-
- include FilepickerRails::Tag
-
- # Creates a javascript tag to the filepicker JavaScript.
- #
- # #### Examples
- #
- # filepicker_js_include_tag
- # # =>
- def filepicker_js_include_tag
- javascript_include_tag "//api.filepicker.io/v2/filepicker.js", type: "text/javascript"
- end
-
- # Creates a filepicker field tag, accepts optional `options` hash for configuration.
- #
- # #### Options
- #
- # - `:button_text` - The text of the upload button.
- # - `:button_class` - The class of the upload button.
- # - `:extensions` - The extensions of file types you want to support for this upload. Ex: `.png,.jpg`.
- # - `:mimetypes` - The file types you want to support for this upload. Ex: `image/png,text/*`.
- # - `:container` - Where to show the file picker dialog can be `modal`, `window` or the id of an iframe on the page.
- # - `:multiple` - (true or false) Whether or not multiple uploads can be saved at once.
- # - `:services` - What services your users can upload to. Ex: `BOX, COMPUTER, FACEBOOK`.
- # - `:store_path` - The path to store the file at within the specified file store.
- # - `:store_location` - The file is not copied by default. It remains in the original location. If you wish you have the file copied onto your own storage, you can specify where we should put the copy. The only value at the moment is `S3`.
- # - `:store_container` - The bucket or container in your specified `store_location`. Defaults to the container specified in the developer portal. Does not apply to Dropbox storage.
- # - `:store_access` - Should the underlying file be publicly available on its S3 link. Options are `public` and `private`, defaults to 'private'.
- # - `:dragdrop` - (`true` or `false`) Whether or not to allow drag-and-drop uploads.
- # - `:drag_text` - The text of the dragdrop pane.
- # - `:drag_class` - The class of the dragdrop pane.
- # - `:onchange` - The onchange event.
- # - `:max_size` - The maximum file size allowed, in bytes.
- # - `:max_files` - The maximum number of files.
- # - `:open_to` - Open the picker to the given service. Ex: `COMPUTER`.
- # - `:class` - Add a class to the input.
- # - `:value` - Define the value of the input
- # - `:language` - Open the picker to the given language. Ex: `fr`.
- #
- # #### Examples
- #
- # filepicker_field_tag('user[filepicker_url]')
- # # =>
- #
- def filepicker_field_tag(name, options = {})
- define_input_options(options)
- tag :input, { 'type' => type, 'name' => name, 'id' => sanitize_to_id(name) }.update(input_options.stringify_keys)
- end
-
- # Creates a button allowing the user to download a file
- # (or upload it to any of the supported services). Set the content of
- # the button on the `text` parameter. The `url` of the content you want the user to save.
- # Define the `mimetype` of the content. Accepts a optional `options` parameter.
- #
- # #### Options
- #
- # - `:container` - Where to show the file picker dialog can be `modal`,
- # `window` or the id of an iframe on the page.
- # - `:services` - What services your users can upload to. Ex: `BOX, COMPUTER, FACEBOOK`.
- # - `:save_as_name` - A recommended file name. The user can override this.
- #
- # #### Examples
- #
- # filepicker_save_button "Save", @user.filepicker_url, "image/jpg"
- # # =>
- #
- def filepicker_save_button(text, url, mimetype, options = {})
- export_widget(text, url, mimetype, options) do
- button_tag(text, options)
- end
- end
-
- # Creates a link allowing the user to download a file
- # (or upload it to any of the supported services). Set the content of
- # the link on the `text` parameter. The `url` of the content you want the user to save.
- # Define the `mimetype` of the content. Accepts a optional `options` parameter.
- #
- # #### Options
- #
- # - `:container` - Where to show the file picker dialog can be `modal`,
- # `window` or the id of an iframe on the page.
- # - `:services` - What services your users can upload to. Ex: `BOX, COMPUTER, FACEBOOK`.
- # - `:save_as_name` - A recommended file name. The user can override this.
- #
- # #### Examples
- #
- # filepicker_save_link "Save", @user.filepicker_url, "image/jpg"
- # # => save
- #
- def filepicker_save_link(text, url, mimetype, options = {})
- export_widget(text, url, mimetype, options) do
- options[:id] = options.fetch(:id, 'filepicker_export_widget_link')
- link_to text, '#', options
- end
- end
-
- # Creates a image tag of the `url`. Accepts the options to work on filepicker.io,
- # see the valid options on `filepicker_image_url` documentation. Accepts html options to the image tag,
- # see the [image_tag](http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-image_tag)
- # documentation for the valid options.
- #
- # #### Examples
- #
- # filepicker_image_tag @user.filepicker_url, w: 160, h: 160, fit: 'clip'
- # # =>
- #
- def filepicker_image_tag(url, image_options={}, image_tag_options={})
- image_tag(filepicker_image_url(url, image_options), image_tag_options)
- end
-
- # Creates the full path of the image to the specified `url` accepts optional `options`
- # hash for configuration.
- #
- # #### Options
- #
- # - `:w` - Resize the image to this width.
- #
- # - `:h` - Resize the image to this height.
- #
- # - `:fit` - Specifies how to resize the image. Possible values are:
- # - `:clip` - Resizes the image to fit within the specified parameters without
- # distorting, cropping, or changing the aspect ratio, this is the default.
- # - `:crop` - Resizes the image to fit the specified parameters exactly by
- # removing any parts of the image that don't fit within the boundaries
- # - `:scales` - Resizes the image to fit the specified parameters exactly by
- # scaling the image to the desired size
- # - `:align` - Determines how the image is aligned when resizing and using the "fit" parameter.
- # Check API for details.
- #
- # - `:rotate` - Rotate the image. Default is no rotation. Possible values are:
- # - `:exif` - will rotate the image automatically based on the exif data in the image.
- # - Other valid values are integers between 0 and 359, for degrees of rotation.
- #
- # - `:crop` - Crops the image to a specified rectangle. The input to this parameter
- # should be 4 numbers for `x,y,width,height` - for example,
- # `10, 20, 200, 250` would select the 200x250 pixel rectangle starting
- # from 10 pixels from the left edge and 20 pixels from the top edge of the
- # image.
- #
- # - `:crop_first` - Makes sure the image is cropped before any other
- # conversion parameters are executed.
- # The only value for this parameter is `true`.
- #
- # - `:format` - Specifies what format the image should be converted to, if any.
- # Possible values are `jpg` and `png`. For `jpg` conversions, you
- # can additionally specify a quality parameter.
- #
- # - `:quality` - For jpeg conversion, specifies the quality of the resultant image.
- # Quality should be an integer between 1 and 100
- #
- # - `:watermark` - Adds the specified absolute url as a watermark on the image.
- #
- # - `:watersize` - This size of the watermark, as a percentage of the base
- # image (not the original watermark).
- #
- # - `:waterposition` - Where to put the watermark relative to the base image.
- # Possible values for vertical position are `top`,`middle`,
- # `bottom` and `left`,`center`,`right`, for horizontal
- # position. The two can be combined by separating vertical
- # and horizontal with a comma. The default behavior
- # is bottom,right
- #
- # - `:cache` - Specifies if the image should be cached or not.
- #
- # - `:compress` - You can take advantage of Filepicker's image compression which utilizes JPEGtran and OptiPNG.
- # The value for this parameter is boolean. If you want to compress your image then the parameter
- # is compress:true. Compression is off/false by default.
- #
- # #### Examples
- #
- # filepicker_image_url @user.filepicker_url, w: 160, h: 160, fit: 'clip'
- # # => https://www.filepicker.io/api/file/hFHUCB3iTxyMzseuWOgG/convert?w=160&h=160&fit=clip
- #
- def filepicker_image_url(url, options = {})
- FilepickerImageUrl.new(url, options).execute
- end
-
- class FilepickerImageUrl
-
- CONVERT_OPTIONS = [:w, :h, :fit, :align, :rotate, :crop, :format,
- :quality, :watermark, :watersize, :waterposition,
- :crop_first]
- VALID_OPTIONS = CONVERT_OPTIONS + [:cache, :compress]
-
- def initialize(url, options = {})
- @url, @options = url, options
- end
-
- def execute
- base_url = url_with_path.split('?').first
- query_params = all_options.to_query
-
- [base_url, query_params.presence].compact.join('?')
- end
-
- private
-
- attr_reader :url, :options
-
- def valid_options
- options.select { |option| VALID_OPTIONS.include?(option) }
- end
-
- def convert_options
- options.select { |option| CONVERT_OPTIONS.include?(option) }
- end
-
- def all_options
- [original_url_options, valid_options, policy_config].inject(&:merge)
- end
-
- def original_url_options
- query_string = url_with_path.split('?')[1]
-
- if query_string
- Rack::Utils.parse_nested_query(query_string)
- else
- {}
- end
- end
-
- def cdn_host
- @cdn_host ||= ::Rails.application.config.filepicker_rails.cdn_host
- end
-
- def cdn_url
- if cdn_host
- uri = URI.parse(url)
- url.gsub("#{uri.scheme}://#{uri.host}", cdn_host)
- else
- url
- end
- end
-
- def handle
- url.split("/").last
- end
-
- def policy_config
- Policy.apply(options: {handle: handle})
- end
-
- def url_with_path
- @url_with_path ||= if append_convert_on_url_path?
- "#{cdn_url}/convert"
- else
- cdn_url
- end
- end
-
- def append_convert_on_url_path?
- convert_options.any? && !cdn_url.match('/convert')
- end
- end
- private_constant :FilepickerImageUrl
-
- private
-
- def export_widget(text, url, mimetype, options, &block)
- options[:data] ||= {}
- container = options.delete(:container)
- services = options.delete(:services)
- save_as = options.delete(:save_as_name)
-
- options[:data]['fp-url'] = url
- options[:data]['fp-apikey'] = ::Rails.application.config.filepicker_rails.api_key
- options[:data]['fp-mimetype'] = mimetype
- options[:data]['fp-container'] = container if container
- options[:data]['fp-services'] = Array(services).join(",") if services
- options[:data]['fp-suggestedFilename'] = save_as if save_as
- block.call
- end
- end
-end
diff --git a/app/helpers/filepicker_rails/form_helper.rb b/app/helpers/filepicker_rails/form_helper.rb
deleted file mode 100644
index c73210a..0000000
--- a/app/helpers/filepicker_rails/form_helper.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-module FilepickerRails
- module FormHelper
-
- include FilepickerRails::Tag
-
- # Creates a filepicker field, accepts optional `options` hash for configuration.
- #
- # #### Options
- #
- # - `:button_text` - The text of the upload button.
- # - `:button_class` - The class of the upload button.
- # - `:extensions` - The extensions of file types you want to support for this upload. Ex: `.png,.jpg`.
- # - `:mimetypes` - The file types you want to support for this upload. Ex: `image/png,text/*`.
- # - `:container` - Where to show the file picker dialog can be `modal`, `window` or the id of an iframe on the page.
- # - `:multiple` - (true or false) Whether or not multiple uploads can be saved at once.
- # - `:services` - What services your users can upload to. Ex: `BOX, COMPUTER, FACEBOOK`.
- # - `:store_path` - The path to store the file at within the specified file store.
- # - `:store_location` - The file is not copied by default. It remains in the original location. If you wish you have the file copied onto your own storage, you can specify where we should put the copy. The only value at the moment is `S3`.
- # - `:store_container` - The bucket or container in your specified `store_location`. Defaults to the container specified in the developer portal. Does not apply to Dropbox storage.
- # - `:store_access` - Should the underlying file be publicly available on its S3 link. Options are `public` and `private`, defaults to 'private'.
- # - `:dragdrop` - (`true` or `false`) Whether or not to allow drag-and-drop uploads.
- # - `:drag_text` - The text of the dragdrop pane.
- # - `:drag_class` - The class of the dragdrop pane.
- # - `:onchange` - The onchange event.
- # - `:max_size` - The maximum file size allowed, in bytes.
- # - `:max_files` - The maximum number of files.
- # - `:open_to` - Open the picker to the given service. Ex: `COMPUTER`.
- # - `:class` - Add a class to the input.
- # - `:value` - Define the value of the input
- # - `:language` - Open the picker to the given language. Ex: `fr`.
- #
- # #### Examples
- #
- # filepicker_field(:filepicker_url)
- # # =>
- #
- # This is mixed on form for to be used like.
- #
- # <%= form_for @user do |f| %>
- # <%= f.filepicker_field :filepicker_url %>
- # <%= f.submit %>
- # <% end %>
- #
- def filepicker_field(method, options = {})
- define_input_options(options)
- @method = method
- if rails_greater_than_4?
- rails_greater_than_4_input
- else
- rails_input
- end
- end
-
- private
-
- attr_reader :method, :object_name, :template
-
- def rails_greater_than_4_input
- tag = ActionView::Helpers::Tags::TextField.new(object_name, method, template, objectify_options(input_options))
- tag.send(:add_default_name_and_id, input_options)
- tag.render
- end
-
- def rails_input
- ActionView::Helpers::InstanceTag.new(object_name, method, template).to_input_field_tag(type, input_options)
- end
-
- def rails_greater_than_4?
- ::Rails.version.to_i >= 4
- end
- end
-end
diff --git a/app/helpers/filestack_rails/application_helper.rb b/app/helpers/filestack_rails/application_helper.rb
new file mode 100644
index 0000000..4a93754
--- /dev/null
+++ b/app/helpers/filestack_rails/application_helper.rb
@@ -0,0 +1,38 @@
+module FilestackRails
+ module ApplicationHelper
+
+ def filestack_js_include_tag
+ javascript_include_tag "https://static.filestackapi.com/v3/filestack.js", type: "text/javascript"
+ end
+
+ def filestack_js_init_tag
+ client_name, apikey = get_client_and_api_key
+ javascript_string = "var #{client_name} = filestack.init('#{apikey}');"
+ javascript_tag javascript_string
+ end
+
+ def filestack_picker_element(content, callback, options = {})
+ button_tag content, onclick: create_javascript_for_picker(callback, options)
+ end
+
+ private
+
+ def create_javascript_for_picker(callback, options)
+ client_name, apikey = get_client_and_api_key
+ json_string = if options.nil?
+ ''
+ else
+ options.to_json
+ end
+ "(function(){
+ #{client_name}.pick(#{json_string}).then(function(data){#{callback}(data)})
+ })()"
+ end
+
+ def get_client_and_api_key
+ client_name = ::Rails.application.config.filestack_rails.client_name
+ apikey = ::Rails::application.config.filestack_rails.api_key
+ [client_name, apikey]
+ end
+ end
+end
diff --git a/app/helpers/filestack_rails/form_helper.rb b/app/helpers/filestack_rails/form_helper.rb
new file mode 100644
index 0000000..3d84dd1
--- /dev/null
+++ b/app/helpers/filestack_rails/form_helper.rb
@@ -0,0 +1,33 @@
+module FilestackRails
+ module FormHelper
+
+ def filestack_field(method, options = {})
+ get_filestack_field_button(method, options)
+ end
+
+ private
+
+ def get_filestack_field_button(method, options)
+ # TODO: add filestack_field button
+ end
+
+ def create_javascript_for_picker(callback, options)
+ client_name, apikey = get_client_and_api_key
+ json_string = if options.nil?
+ ''
+ else
+ options.to_json
+ end
+ "(function(){
+ #{client_name}.pick(#{json_string}).then(function(data){#{callback}(data)})
+ })()"
+ end
+
+ def get_client_and_api_key
+ client_name = ::Rails.application.config.filestack_rails.client_name
+ apikey = ::Rails::application.config.filestack_rails.api_key
+ [client_name, apikey]
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/filestack_rails/application_job.rb b/app/jobs/filestack_rails/application_job.rb
new file mode 100644
index 0000000..0f89a75
--- /dev/null
+++ b/app/jobs/filestack_rails/application_job.rb
@@ -0,0 +1,4 @@
+module FilestackRails
+ class ApplicationJob < ActiveJob::Base
+ end
+end
diff --git a/app/mailers/filestack_rails/application_mailer.rb b/app/mailers/filestack_rails/application_mailer.rb
new file mode 100644
index 0000000..1374d1d
--- /dev/null
+++ b/app/mailers/filestack_rails/application_mailer.rb
@@ -0,0 +1,6 @@
+module FilestackRails
+ class ApplicationMailer < ActionMailer::Base
+ default from: 'from@example.com'
+ layout 'mailer'
+ end
+end
diff --git a/app/models/filestack_rails/application_record.rb b/app/models/filestack_rails/application_record.rb
new file mode 100644
index 0000000..b195360
--- /dev/null
+++ b/app/models/filestack_rails/application_record.rb
@@ -0,0 +1,5 @@
+module FilestackRails
+ class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+ end
+end
diff --git a/app/views/layouts/filestack_rails/application.html.erb b/app/views/layouts/filestack_rails/application.html.erb
new file mode 100644
index 0000000..b35a669
--- /dev/null
+++ b/app/views/layouts/filestack_rails/application.html.erb
@@ -0,0 +1,14 @@
+
+
+
You may have mistyped the address or the page may have moved.
+You may have mistyped the address or the page may have moved.
+If you are the application owner check the logs for more information.
If you are the application owner check the logs for more information.
diff --git a/spec/dummy/public/422.html b/spec/dummy/public/422.html index fbb4b84..c08eac0 100644 --- a/spec/dummy/public/422.html +++ b/spec/dummy/public/422.html @@ -2,17 +2,23 @@Maybe you tried to change something you didn't have access to.
+Maybe you tried to change something you didn't have access to.
+If you are the application owner check the logs for more information.
If you are the application owner check the logs for more information.
diff --git a/spec/dummy/public/500.html b/spec/dummy/public/500.html index e9052d3..78a030a 100644 --- a/spec/dummy/public/500.html +++ b/spec/dummy/public/500.html @@ -2,17 +2,23 @@If you are the application owner check the logs for more information.
If you are the application owner check the logs for more information.
diff --git a/spec/dummy/db/.gitkeep b/spec/dummy/public/apple-touch-icon-precomposed.png similarity index 100% rename from spec/dummy/db/.gitkeep rename to spec/dummy/public/apple-touch-icon-precomposed.png diff --git a/spec/dummy/public/apple-touch-icon.png b/spec/dummy/public/apple-touch-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/public/assets/.sprockets-manifest-51e415a5c87d94b11edfa7efa226c696.json b/spec/dummy/public/assets/.sprockets-manifest-51e415a5c87d94b11edfa7efa226c696.json new file mode 100644 index 0000000..a7fa1bf --- /dev/null +++ b/spec/dummy/public/assets/.sprockets-manifest-51e415a5c87d94b11edfa7efa226c696.json @@ -0,0 +1 @@ +{"files":{"application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js":{"logical_path":"application.js","mtime":"2017-07-15T20:59:47-05:00","size":21494,"digest":"60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e","integrity":"sha256-YMYCDvrLAjrfSpKF3M9sI8BpS6vF2yEMSQbsecH2mk4="},"application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css":{"logical_path":"application.css","mtime":"2017-07-15T20:06:06-05:00","size":703,"digest":"af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886","integrity":"sha256-rwSyJv1yAt/FMs567blaASgneTfpDTs6PTXhzOnhaIY="},"filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js":{"logical_path":"filestack_rails/application.js","mtime":"2017-07-15T20:34:56-05:00","size":661,"digest":"d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7","integrity":"sha256-0tnD4HMwnD+w3vDElAnDXCT8rnKYU0NYE1ofX37R27c="},"filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css":{"logical_path":"filestack_rails/application.css","mtime":"2017-07-15T20:06:06-05:00","size":703,"digest":"af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886","integrity":"sha256-rwSyJv1yAt/FMs567blaASgneTfpDTs6PTXhzOnhaIY="},"application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js":{"logical_path":"application.js","mtime":"2017-07-15T21:01:48-05:00","size":21496,"digest":"29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2","integrity":"sha256-KfBK24W8+3OCDJRr6FDNPKVZsu2AaHY0rIL/5OcN4KI="},"application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js":{"logical_path":"application.js","mtime":"2017-07-15T21:13:58-05:00","size":21538,"digest":"e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a","integrity":"sha256-4DhZlvjF4sZ1XQwVcSpcigVDqlPthWu6L3NMgdnizoo="}},"assets":{"application.js":"application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js","application.css":"application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css","filestack_rails/application.js":"filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js","filestack_rails/application.css":"filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css"}} \ No newline at end of file diff --git a/spec/dummy/public/assets/application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js b/spec/dummy/public/assets/application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js new file mode 100644 index 0000000..6d38e5f --- /dev/null +++ b/spec/dummy/public/assets/application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js @@ -0,0 +1,627 @@ +console.log('this is working'); +(function() { + (function() { + (function() { + var slice = [].slice; + + this.ActionCable = { + INTERNAL: { + "message_types": { + "welcome": "welcome", + "ping": "ping", + "confirmation": "confirm_subscription", + "rejection": "reject_subscription" + }, + "default_mount_path": "/cable", + "protocols": ["actioncable-v1-json", "actioncable-unsupported"] + }, + WebSocket: window.WebSocket, + logger: window.console, + createConsumer: function(url) { + var ref; + if (url == null) { + url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path; + } + return new ActionCable.Consumer(this.createWebSocketURL(url)); + }, + getConfig: function(name) { + var element; + element = document.head.querySelector("meta[name='action-cable-" + name + "']"); + return element != null ? element.getAttribute("content") : void 0; + }, + createWebSocketURL: function(url) { + var a; + if (url && !/^wss?:/i.test(url)) { + a = document.createElement("a"); + a.href = url; + a.href = a.href; + a.protocol = a.protocol.replace("http", "ws"); + return a.href; + } else { + return url; + } + }, + startDebugging: function() { + return this.debugging = true; + }, + stopDebugging: function() { + return this.debugging = null; + }, + log: function() { + var messages, ref; + messages = 1 <= arguments.length ? slice.call(arguments, 0) : []; + if (this.debugging) { + messages.push(Date.now()); + return (ref = this.logger).log.apply(ref, ["[ActionCable]"].concat(slice.call(messages))); + } + } + }; + + }).call(this); + }).call(this); + + var ActionCable = this.ActionCable; + + (function() { + (function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + ActionCable.ConnectionMonitor = (function() { + var clamp, now, secondsSince; + + ConnectionMonitor.pollInterval = { + min: 3, + max: 30 + }; + + ConnectionMonitor.staleThreshold = 6; + + function ConnectionMonitor(connection) { + this.connection = connection; + this.visibilityDidChange = bind(this.visibilityDidChange, this); + this.reconnectAttempts = 0; + } + + ConnectionMonitor.prototype.start = function() { + if (!this.isRunning()) { + this.startedAt = now(); + delete this.stoppedAt; + this.startPolling(); + document.addEventListener("visibilitychange", this.visibilityDidChange); + return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms"); + } + }; + + ConnectionMonitor.prototype.stop = function() { + if (this.isRunning()) { + this.stoppedAt = now(); + this.stopPolling(); + document.removeEventListener("visibilitychange", this.visibilityDidChange); + return ActionCable.log("ConnectionMonitor stopped"); + } + }; + + ConnectionMonitor.prototype.isRunning = function() { + return (this.startedAt != null) && (this.stoppedAt == null); + }; + + ConnectionMonitor.prototype.recordPing = function() { + return this.pingedAt = now(); + }; + + ConnectionMonitor.prototype.recordConnect = function() { + this.reconnectAttempts = 0; + this.recordPing(); + delete this.disconnectedAt; + return ActionCable.log("ConnectionMonitor recorded connect"); + }; + + ConnectionMonitor.prototype.recordDisconnect = function() { + this.disconnectedAt = now(); + return ActionCable.log("ConnectionMonitor recorded disconnect"); + }; + + ConnectionMonitor.prototype.startPolling = function() { + this.stopPolling(); + return this.poll(); + }; + + ConnectionMonitor.prototype.stopPolling = function() { + return clearTimeout(this.pollTimeout); + }; + + ConnectionMonitor.prototype.poll = function() { + return this.pollTimeout = setTimeout((function(_this) { + return function() { + _this.reconnectIfStale(); + return _this.poll(); + }; + })(this), this.getPollInterval()); + }; + + ConnectionMonitor.prototype.getPollInterval = function() { + var interval, max, min, ref; + ref = this.constructor.pollInterval, min = ref.min, max = ref.max; + interval = 5 * Math.log(this.reconnectAttempts + 1); + return Math.round(clamp(interval, min, max) * 1000); + }; + + ConnectionMonitor.prototype.reconnectIfStale = function() { + if (this.connectionIsStale()) { + ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + (this.getPollInterval()) + " ms, time disconnected = " + (secondsSince(this.disconnectedAt)) + " s, stale threshold = " + this.constructor.staleThreshold + " s"); + this.reconnectAttempts++; + if (this.disconnectedRecently()) { + return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect"); + } else { + ActionCable.log("ConnectionMonitor reopening"); + return this.connection.reopen(); + } + } + }; + + ConnectionMonitor.prototype.connectionIsStale = function() { + var ref; + return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold; + }; + + ConnectionMonitor.prototype.disconnectedRecently = function() { + return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold; + }; + + ConnectionMonitor.prototype.visibilityDidChange = function() { + if (document.visibilityState === "visible") { + return setTimeout((function(_this) { + return function() { + if (_this.connectionIsStale() || !_this.connection.isOpen()) { + ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState); + return _this.connection.reopen(); + } + }; + })(this), 200); + } + }; + + now = function() { + return new Date().getTime(); + }; + + secondsSince = function(time) { + return (now() - time) / 1000; + }; + + clamp = function(number, min, max) { + return Math.max(min, Math.min(max, number)); + }; + + return ConnectionMonitor; + + })(); + + }).call(this); + (function() { + var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol, + slice = [].slice, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols; + + supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++]; + + ActionCable.Connection = (function() { + Connection.reopenDelay = 500; + + function Connection(consumer) { + this.consumer = consumer; + this.open = bind(this.open, this); + this.subscriptions = this.consumer.subscriptions; + this.monitor = new ActionCable.ConnectionMonitor(this); + this.disconnected = true; + } + + Connection.prototype.send = function(data) { + if (this.isOpen()) { + this.webSocket.send(JSON.stringify(data)); + return true; + } else { + return false; + } + }; + + Connection.prototype.open = function() { + if (this.isActive()) { + ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState())); + return false; + } else { + ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols); + if (this.webSocket != null) { + this.uninstallEventHandlers(); + } + this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols); + this.installEventHandlers(); + this.monitor.start(); + return true; + } + }; + + Connection.prototype.close = function(arg) { + var allowReconnect, ref1; + allowReconnect = (arg != null ? arg : { + allowReconnect: true + }).allowReconnect; + if (!allowReconnect) { + this.monitor.stop(); + } + if (this.isActive()) { + return (ref1 = this.webSocket) != null ? ref1.close() : void 0; + } + }; + + Connection.prototype.reopen = function() { + var error; + ActionCable.log("Reopening WebSocket, current state is " + (this.getState())); + if (this.isActive()) { + try { + return this.close(); + } catch (error1) { + error = error1; + return ActionCable.log("Failed to reopen WebSocket", error); + } finally { + ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms"); + setTimeout(this.open, this.constructor.reopenDelay); + } + } else { + return this.open(); + } + }; + + Connection.prototype.getProtocol = function() { + var ref1; + return (ref1 = this.webSocket) != null ? ref1.protocol : void 0; + }; + + Connection.prototype.isOpen = function() { + return this.isState("open"); + }; + + Connection.prototype.isActive = function() { + return this.isState("open", "connecting"); + }; + + Connection.prototype.isProtocolSupported = function() { + var ref1; + return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0; + }; + + Connection.prototype.isState = function() { + var ref1, states; + states = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return ref1 = this.getState(), indexOf.call(states, ref1) >= 0; + }; + + Connection.prototype.getState = function() { + var ref1, state, value; + for (state in WebSocket) { + value = WebSocket[state]; + if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) { + return state.toLowerCase(); + } + } + return null; + }; + + Connection.prototype.installEventHandlers = function() { + var eventName, handler; + for (eventName in this.events) { + handler = this.events[eventName].bind(this); + this.webSocket["on" + eventName] = handler; + } + }; + + Connection.prototype.uninstallEventHandlers = function() { + var eventName; + for (eventName in this.events) { + this.webSocket["on" + eventName] = function() {}; + } + }; + + Connection.prototype.events = { + message: function(event) { + var identifier, message, ref1, type; + if (!this.isProtocolSupported()) { + return; + } + ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type; + switch (type) { + case message_types.welcome: + this.monitor.recordConnect(); + return this.subscriptions.reload(); + case message_types.ping: + return this.monitor.recordPing(); + case message_types.confirmation: + return this.subscriptions.notify(identifier, "connected"); + case message_types.rejection: + return this.subscriptions.reject(identifier); + default: + return this.subscriptions.notify(identifier, "received", message); + } + }, + open: function() { + ActionCable.log("WebSocket onopen event, using '" + (this.getProtocol()) + "' subprotocol"); + this.disconnected = false; + if (!this.isProtocolSupported()) { + ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting."); + return this.close({ + allowReconnect: false + }); + } + }, + close: function(event) { + ActionCable.log("WebSocket onclose event"); + if (this.disconnected) { + return; + } + this.disconnected = true; + this.monitor.recordDisconnect(); + return this.subscriptions.notifyAll("disconnected", { + willAttemptReconnect: this.monitor.isRunning() + }); + }, + error: function() { + return ActionCable.log("WebSocket onerror event"); + } + }; + + return Connection; + + })(); + + }).call(this); + (function() { + var slice = [].slice; + + ActionCable.Subscriptions = (function() { + function Subscriptions(consumer) { + this.consumer = consumer; + this.subscriptions = []; + } + + Subscriptions.prototype.create = function(channelName, mixin) { + var channel, params, subscription; + channel = channelName; + params = typeof channel === "object" ? channel : { + channel: channel + }; + subscription = new ActionCable.Subscription(this.consumer, params, mixin); + return this.add(subscription); + }; + + Subscriptions.prototype.add = function(subscription) { + this.subscriptions.push(subscription); + this.consumer.ensureActiveConnection(); + this.notify(subscription, "initialized"); + this.sendCommand(subscription, "subscribe"); + return subscription; + }; + + Subscriptions.prototype.remove = function(subscription) { + this.forget(subscription); + if (!this.findAll(subscription.identifier).length) { + this.sendCommand(subscription, "unsubscribe"); + } + return subscription; + }; + + Subscriptions.prototype.reject = function(identifier) { + var i, len, ref, results, subscription; + ref = this.findAll(identifier); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + this.forget(subscription); + this.notify(subscription, "rejected"); + results.push(subscription); + } + return results; + }; + + Subscriptions.prototype.forget = function(subscription) { + var s; + this.subscriptions = (function() { + var i, len, ref, results; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + if (s !== subscription) { + results.push(s); + } + } + return results; + }).call(this); + return subscription; + }; + + Subscriptions.prototype.findAll = function(identifier) { + var i, len, ref, results, s; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + if (s.identifier === identifier) { + results.push(s); + } + } + return results; + }; + + Subscriptions.prototype.reload = function() { + var i, len, ref, results, subscription; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + results.push(this.sendCommand(subscription, "subscribe")); + } + return results; + }; + + Subscriptions.prototype.notifyAll = function() { + var args, callbackName, i, len, ref, results, subscription; + callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args)))); + } + return results; + }; + + Subscriptions.prototype.notify = function() { + var args, callbackName, i, len, results, subscription, subscriptions; + subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; + if (typeof subscription === "string") { + subscriptions = this.findAll(subscription); + } else { + subscriptions = [subscription]; + } + results = []; + for (i = 0, len = subscriptions.length; i < len; i++) { + subscription = subscriptions[i]; + results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0); + } + return results; + }; + + Subscriptions.prototype.sendCommand = function(subscription, command) { + var identifier; + identifier = subscription.identifier; + return this.consumer.send({ + command: command, + identifier: identifier + }); + }; + + return Subscriptions; + + })(); + + }).call(this); + (function() { + ActionCable.Subscription = (function() { + var extend; + + function Subscription(consumer, params, mixin) { + this.consumer = consumer; + if (params == null) { + params = {}; + } + this.identifier = JSON.stringify(params); + extend(this, mixin); + } + + Subscription.prototype.perform = function(action, data) { + if (data == null) { + data = {}; + } + data.action = action; + return this.send(data); + }; + + Subscription.prototype.send = function(data) { + return this.consumer.send({ + command: "message", + identifier: this.identifier, + data: JSON.stringify(data) + }); + }; + + Subscription.prototype.unsubscribe = function() { + return this.consumer.subscriptions.remove(this); + }; + + extend = function(object, properties) { + var key, value; + if (properties != null) { + for (key in properties) { + value = properties[key]; + object[key] = value; + } + } + return object; + }; + + return Subscription; + + })(); + + }).call(this); + (function() { + ActionCable.Consumer = (function() { + function Consumer(url) { + this.url = url; + this.subscriptions = new ActionCable.Subscriptions(this); + this.connection = new ActionCable.Connection(this); + } + + Consumer.prototype.send = function(data) { + return this.connection.send(data); + }; + + Consumer.prototype.connect = function() { + return this.connection.open(); + }; + + Consumer.prototype.disconnect = function() { + return this.connection.close({ + allowReconnect: false + }); + }; + + Consumer.prototype.ensureActiveConnection = function() { + if (!this.connection.isActive()) { + return this.connection.open(); + } + }; + + return Consumer; + + })(); + + }).call(this); + }).call(this); + + if (typeof module === "object" && module.exports) { + module.exports = ActionCable; + } else if (typeof define === "function" && define.amd) { + define(ActionCable); + } +}).call(this); +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// + + + + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// + +; diff --git a/spec/dummy/public/assets/application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js.gz b/spec/dummy/public/assets/application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js.gz new file mode 100644 index 0000000..8620f56 Binary files /dev/null and b/spec/dummy/public/assets/application-29f04adb85bcfb73820c946be850cd3ca559b2ed80687634ac82ffe4e70de0a2.js.gz differ diff --git a/spec/dummy/public/assets/application-5ac6384ca459dff358234a54a3a7c0a1e0031a928018b37af5408111e7077bb9.css.54141996.11036.294896 b/spec/dummy/public/assets/application-5ac6384ca459dff358234a54a3a7c0a1e0031a928018b37af5408111e7077bb9.css.54141996.11036.294896 new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/public/assets/application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js b/spec/dummy/public/assets/application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js new file mode 100644 index 0000000..8a27e93 --- /dev/null +++ b/spec/dummy/public/assets/application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js @@ -0,0 +1,626 @@ +(function() { + (function() { + (function() { + var slice = [].slice; + + this.ActionCable = { + INTERNAL: { + "message_types": { + "welcome": "welcome", + "ping": "ping", + "confirmation": "confirm_subscription", + "rejection": "reject_subscription" + }, + "default_mount_path": "/cable", + "protocols": ["actioncable-v1-json", "actioncable-unsupported"] + }, + WebSocket: window.WebSocket, + logger: window.console, + createConsumer: function(url) { + var ref; + if (url == null) { + url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path; + } + return new ActionCable.Consumer(this.createWebSocketURL(url)); + }, + getConfig: function(name) { + var element; + element = document.head.querySelector("meta[name='action-cable-" + name + "']"); + return element != null ? element.getAttribute("content") : void 0; + }, + createWebSocketURL: function(url) { + var a; + if (url && !/^wss?:/i.test(url)) { + a = document.createElement("a"); + a.href = url; + a.href = a.href; + a.protocol = a.protocol.replace("http", "ws"); + return a.href; + } else { + return url; + } + }, + startDebugging: function() { + return this.debugging = true; + }, + stopDebugging: function() { + return this.debugging = null; + }, + log: function() { + var messages, ref; + messages = 1 <= arguments.length ? slice.call(arguments, 0) : []; + if (this.debugging) { + messages.push(Date.now()); + return (ref = this.logger).log.apply(ref, ["[ActionCable]"].concat(slice.call(messages))); + } + } + }; + + }).call(this); + }).call(this); + + var ActionCable = this.ActionCable; + + (function() { + (function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + ActionCable.ConnectionMonitor = (function() { + var clamp, now, secondsSince; + + ConnectionMonitor.pollInterval = { + min: 3, + max: 30 + }; + + ConnectionMonitor.staleThreshold = 6; + + function ConnectionMonitor(connection) { + this.connection = connection; + this.visibilityDidChange = bind(this.visibilityDidChange, this); + this.reconnectAttempts = 0; + } + + ConnectionMonitor.prototype.start = function() { + if (!this.isRunning()) { + this.startedAt = now(); + delete this.stoppedAt; + this.startPolling(); + document.addEventListener("visibilitychange", this.visibilityDidChange); + return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms"); + } + }; + + ConnectionMonitor.prototype.stop = function() { + if (this.isRunning()) { + this.stoppedAt = now(); + this.stopPolling(); + document.removeEventListener("visibilitychange", this.visibilityDidChange); + return ActionCable.log("ConnectionMonitor stopped"); + } + }; + + ConnectionMonitor.prototype.isRunning = function() { + return (this.startedAt != null) && (this.stoppedAt == null); + }; + + ConnectionMonitor.prototype.recordPing = function() { + return this.pingedAt = now(); + }; + + ConnectionMonitor.prototype.recordConnect = function() { + this.reconnectAttempts = 0; + this.recordPing(); + delete this.disconnectedAt; + return ActionCable.log("ConnectionMonitor recorded connect"); + }; + + ConnectionMonitor.prototype.recordDisconnect = function() { + this.disconnectedAt = now(); + return ActionCable.log("ConnectionMonitor recorded disconnect"); + }; + + ConnectionMonitor.prototype.startPolling = function() { + this.stopPolling(); + return this.poll(); + }; + + ConnectionMonitor.prototype.stopPolling = function() { + return clearTimeout(this.pollTimeout); + }; + + ConnectionMonitor.prototype.poll = function() { + return this.pollTimeout = setTimeout((function(_this) { + return function() { + _this.reconnectIfStale(); + return _this.poll(); + }; + })(this), this.getPollInterval()); + }; + + ConnectionMonitor.prototype.getPollInterval = function() { + var interval, max, min, ref; + ref = this.constructor.pollInterval, min = ref.min, max = ref.max; + interval = 5 * Math.log(this.reconnectAttempts + 1); + return Math.round(clamp(interval, min, max) * 1000); + }; + + ConnectionMonitor.prototype.reconnectIfStale = function() { + if (this.connectionIsStale()) { + ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + (this.getPollInterval()) + " ms, time disconnected = " + (secondsSince(this.disconnectedAt)) + " s, stale threshold = " + this.constructor.staleThreshold + " s"); + this.reconnectAttempts++; + if (this.disconnectedRecently()) { + return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect"); + } else { + ActionCable.log("ConnectionMonitor reopening"); + return this.connection.reopen(); + } + } + }; + + ConnectionMonitor.prototype.connectionIsStale = function() { + var ref; + return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold; + }; + + ConnectionMonitor.prototype.disconnectedRecently = function() { + return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold; + }; + + ConnectionMonitor.prototype.visibilityDidChange = function() { + if (document.visibilityState === "visible") { + return setTimeout((function(_this) { + return function() { + if (_this.connectionIsStale() || !_this.connection.isOpen()) { + ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState); + return _this.connection.reopen(); + } + }; + })(this), 200); + } + }; + + now = function() { + return new Date().getTime(); + }; + + secondsSince = function(time) { + return (now() - time) / 1000; + }; + + clamp = function(number, min, max) { + return Math.max(min, Math.min(max, number)); + }; + + return ConnectionMonitor; + + })(); + + }).call(this); + (function() { + var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol, + slice = [].slice, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols; + + supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++]; + + ActionCable.Connection = (function() { + Connection.reopenDelay = 500; + + function Connection(consumer) { + this.consumer = consumer; + this.open = bind(this.open, this); + this.subscriptions = this.consumer.subscriptions; + this.monitor = new ActionCable.ConnectionMonitor(this); + this.disconnected = true; + } + + Connection.prototype.send = function(data) { + if (this.isOpen()) { + this.webSocket.send(JSON.stringify(data)); + return true; + } else { + return false; + } + }; + + Connection.prototype.open = function() { + if (this.isActive()) { + ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState())); + return false; + } else { + ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols); + if (this.webSocket != null) { + this.uninstallEventHandlers(); + } + this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols); + this.installEventHandlers(); + this.monitor.start(); + return true; + } + }; + + Connection.prototype.close = function(arg) { + var allowReconnect, ref1; + allowReconnect = (arg != null ? arg : { + allowReconnect: true + }).allowReconnect; + if (!allowReconnect) { + this.monitor.stop(); + } + if (this.isActive()) { + return (ref1 = this.webSocket) != null ? ref1.close() : void 0; + } + }; + + Connection.prototype.reopen = function() { + var error; + ActionCable.log("Reopening WebSocket, current state is " + (this.getState())); + if (this.isActive()) { + try { + return this.close(); + } catch (error1) { + error = error1; + return ActionCable.log("Failed to reopen WebSocket", error); + } finally { + ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms"); + setTimeout(this.open, this.constructor.reopenDelay); + } + } else { + return this.open(); + } + }; + + Connection.prototype.getProtocol = function() { + var ref1; + return (ref1 = this.webSocket) != null ? ref1.protocol : void 0; + }; + + Connection.prototype.isOpen = function() { + return this.isState("open"); + }; + + Connection.prototype.isActive = function() { + return this.isState("open", "connecting"); + }; + + Connection.prototype.isProtocolSupported = function() { + var ref1; + return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0; + }; + + Connection.prototype.isState = function() { + var ref1, states; + states = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return ref1 = this.getState(), indexOf.call(states, ref1) >= 0; + }; + + Connection.prototype.getState = function() { + var ref1, state, value; + for (state in WebSocket) { + value = WebSocket[state]; + if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) { + return state.toLowerCase(); + } + } + return null; + }; + + Connection.prototype.installEventHandlers = function() { + var eventName, handler; + for (eventName in this.events) { + handler = this.events[eventName].bind(this); + this.webSocket["on" + eventName] = handler; + } + }; + + Connection.prototype.uninstallEventHandlers = function() { + var eventName; + for (eventName in this.events) { + this.webSocket["on" + eventName] = function() {}; + } + }; + + Connection.prototype.events = { + message: function(event) { + var identifier, message, ref1, type; + if (!this.isProtocolSupported()) { + return; + } + ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type; + switch (type) { + case message_types.welcome: + this.monitor.recordConnect(); + return this.subscriptions.reload(); + case message_types.ping: + return this.monitor.recordPing(); + case message_types.confirmation: + return this.subscriptions.notify(identifier, "connected"); + case message_types.rejection: + return this.subscriptions.reject(identifier); + default: + return this.subscriptions.notify(identifier, "received", message); + } + }, + open: function() { + ActionCable.log("WebSocket onopen event, using '" + (this.getProtocol()) + "' subprotocol"); + this.disconnected = false; + if (!this.isProtocolSupported()) { + ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting."); + return this.close({ + allowReconnect: false + }); + } + }, + close: function(event) { + ActionCable.log("WebSocket onclose event"); + if (this.disconnected) { + return; + } + this.disconnected = true; + this.monitor.recordDisconnect(); + return this.subscriptions.notifyAll("disconnected", { + willAttemptReconnect: this.monitor.isRunning() + }); + }, + error: function() { + return ActionCable.log("WebSocket onerror event"); + } + }; + + return Connection; + + })(); + + }).call(this); + (function() { + var slice = [].slice; + + ActionCable.Subscriptions = (function() { + function Subscriptions(consumer) { + this.consumer = consumer; + this.subscriptions = []; + } + + Subscriptions.prototype.create = function(channelName, mixin) { + var channel, params, subscription; + channel = channelName; + params = typeof channel === "object" ? channel : { + channel: channel + }; + subscription = new ActionCable.Subscription(this.consumer, params, mixin); + return this.add(subscription); + }; + + Subscriptions.prototype.add = function(subscription) { + this.subscriptions.push(subscription); + this.consumer.ensureActiveConnection(); + this.notify(subscription, "initialized"); + this.sendCommand(subscription, "subscribe"); + return subscription; + }; + + Subscriptions.prototype.remove = function(subscription) { + this.forget(subscription); + if (!this.findAll(subscription.identifier).length) { + this.sendCommand(subscription, "unsubscribe"); + } + return subscription; + }; + + Subscriptions.prototype.reject = function(identifier) { + var i, len, ref, results, subscription; + ref = this.findAll(identifier); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + this.forget(subscription); + this.notify(subscription, "rejected"); + results.push(subscription); + } + return results; + }; + + Subscriptions.prototype.forget = function(subscription) { + var s; + this.subscriptions = (function() { + var i, len, ref, results; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + if (s !== subscription) { + results.push(s); + } + } + return results; + }).call(this); + return subscription; + }; + + Subscriptions.prototype.findAll = function(identifier) { + var i, len, ref, results, s; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + if (s.identifier === identifier) { + results.push(s); + } + } + return results; + }; + + Subscriptions.prototype.reload = function() { + var i, len, ref, results, subscription; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + results.push(this.sendCommand(subscription, "subscribe")); + } + return results; + }; + + Subscriptions.prototype.notifyAll = function() { + var args, callbackName, i, len, ref, results, subscription; + callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args)))); + } + return results; + }; + + Subscriptions.prototype.notify = function() { + var args, callbackName, i, len, results, subscription, subscriptions; + subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; + if (typeof subscription === "string") { + subscriptions = this.findAll(subscription); + } else { + subscriptions = [subscription]; + } + results = []; + for (i = 0, len = subscriptions.length; i < len; i++) { + subscription = subscriptions[i]; + results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0); + } + return results; + }; + + Subscriptions.prototype.sendCommand = function(subscription, command) { + var identifier; + identifier = subscription.identifier; + return this.consumer.send({ + command: command, + identifier: identifier + }); + }; + + return Subscriptions; + + })(); + + }).call(this); + (function() { + ActionCable.Subscription = (function() { + var extend; + + function Subscription(consumer, params, mixin) { + this.consumer = consumer; + if (params == null) { + params = {}; + } + this.identifier = JSON.stringify(params); + extend(this, mixin); + } + + Subscription.prototype.perform = function(action, data) { + if (data == null) { + data = {}; + } + data.action = action; + return this.send(data); + }; + + Subscription.prototype.send = function(data) { + return this.consumer.send({ + command: "message", + identifier: this.identifier, + data: JSON.stringify(data) + }); + }; + + Subscription.prototype.unsubscribe = function() { + return this.consumer.subscriptions.remove(this); + }; + + extend = function(object, properties) { + var key, value; + if (properties != null) { + for (key in properties) { + value = properties[key]; + object[key] = value; + } + } + return object; + }; + + return Subscription; + + })(); + + }).call(this); + (function() { + ActionCable.Consumer = (function() { + function Consumer(url) { + this.url = url; + this.subscriptions = new ActionCable.Subscriptions(this); + this.connection = new ActionCable.Connection(this); + } + + Consumer.prototype.send = function(data) { + return this.connection.send(data); + }; + + Consumer.prototype.connect = function() { + return this.connection.open(); + }; + + Consumer.prototype.disconnect = function() { + return this.connection.close({ + allowReconnect: false + }); + }; + + Consumer.prototype.ensureActiveConnection = function() { + if (!this.connection.isActive()) { + return this.connection.open(); + } + }; + + return Consumer; + + })(); + + }).call(this); + }).call(this); + + if (typeof module === "object" && module.exports) { + module.exports = ActionCable; + } else if (typeof define === "function" && define.amd) { + define(ActionCable); + } +}).call(this); +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// + + + + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// + +console.log('this is working'); diff --git a/spec/dummy/public/assets/application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js.gz b/spec/dummy/public/assets/application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js.gz new file mode 100644 index 0000000..b54aea0 Binary files /dev/null and b/spec/dummy/public/assets/application-60c6020efacb023adf4a9285dccf6c23c0694babc5db210c4906ec79c1f69a4e.js.gz differ diff --git a/spec/dummy/public/assets/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css b/spec/dummy/public/assets/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css new file mode 100644 index 0000000..5e987a2 --- /dev/null +++ b/spec/dummy/public/assets/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css @@ -0,0 +1,16 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + + + */ + diff --git a/spec/dummy/public/assets/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css.gz b/spec/dummy/public/assets/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css.gz new file mode 100644 index 0000000..e7eaa74 Binary files /dev/null and b/spec/dummy/public/assets/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css.gz differ diff --git a/spec/dummy/public/assets/application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js b/spec/dummy/public/assets/application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js new file mode 100644 index 0000000..69a2778 --- /dev/null +++ b/spec/dummy/public/assets/application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js @@ -0,0 +1,630 @@ +document.onload = (function(){ + console.log('this is working'); +})() +; +(function() { + (function() { + (function() { + var slice = [].slice; + + this.ActionCable = { + INTERNAL: { + "message_types": { + "welcome": "welcome", + "ping": "ping", + "confirmation": "confirm_subscription", + "rejection": "reject_subscription" + }, + "default_mount_path": "/cable", + "protocols": ["actioncable-v1-json", "actioncable-unsupported"] + }, + WebSocket: window.WebSocket, + logger: window.console, + createConsumer: function(url) { + var ref; + if (url == null) { + url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path; + } + return new ActionCable.Consumer(this.createWebSocketURL(url)); + }, + getConfig: function(name) { + var element; + element = document.head.querySelector("meta[name='action-cable-" + name + "']"); + return element != null ? element.getAttribute("content") : void 0; + }, + createWebSocketURL: function(url) { + var a; + if (url && !/^wss?:/i.test(url)) { + a = document.createElement("a"); + a.href = url; + a.href = a.href; + a.protocol = a.protocol.replace("http", "ws"); + return a.href; + } else { + return url; + } + }, + startDebugging: function() { + return this.debugging = true; + }, + stopDebugging: function() { + return this.debugging = null; + }, + log: function() { + var messages, ref; + messages = 1 <= arguments.length ? slice.call(arguments, 0) : []; + if (this.debugging) { + messages.push(Date.now()); + return (ref = this.logger).log.apply(ref, ["[ActionCable]"].concat(slice.call(messages))); + } + } + }; + + }).call(this); + }).call(this); + + var ActionCable = this.ActionCable; + + (function() { + (function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + ActionCable.ConnectionMonitor = (function() { + var clamp, now, secondsSince; + + ConnectionMonitor.pollInterval = { + min: 3, + max: 30 + }; + + ConnectionMonitor.staleThreshold = 6; + + function ConnectionMonitor(connection) { + this.connection = connection; + this.visibilityDidChange = bind(this.visibilityDidChange, this); + this.reconnectAttempts = 0; + } + + ConnectionMonitor.prototype.start = function() { + if (!this.isRunning()) { + this.startedAt = now(); + delete this.stoppedAt; + this.startPolling(); + document.addEventListener("visibilitychange", this.visibilityDidChange); + return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms"); + } + }; + + ConnectionMonitor.prototype.stop = function() { + if (this.isRunning()) { + this.stoppedAt = now(); + this.stopPolling(); + document.removeEventListener("visibilitychange", this.visibilityDidChange); + return ActionCable.log("ConnectionMonitor stopped"); + } + }; + + ConnectionMonitor.prototype.isRunning = function() { + return (this.startedAt != null) && (this.stoppedAt == null); + }; + + ConnectionMonitor.prototype.recordPing = function() { + return this.pingedAt = now(); + }; + + ConnectionMonitor.prototype.recordConnect = function() { + this.reconnectAttempts = 0; + this.recordPing(); + delete this.disconnectedAt; + return ActionCable.log("ConnectionMonitor recorded connect"); + }; + + ConnectionMonitor.prototype.recordDisconnect = function() { + this.disconnectedAt = now(); + return ActionCable.log("ConnectionMonitor recorded disconnect"); + }; + + ConnectionMonitor.prototype.startPolling = function() { + this.stopPolling(); + return this.poll(); + }; + + ConnectionMonitor.prototype.stopPolling = function() { + return clearTimeout(this.pollTimeout); + }; + + ConnectionMonitor.prototype.poll = function() { + return this.pollTimeout = setTimeout((function(_this) { + return function() { + _this.reconnectIfStale(); + return _this.poll(); + }; + })(this), this.getPollInterval()); + }; + + ConnectionMonitor.prototype.getPollInterval = function() { + var interval, max, min, ref; + ref = this.constructor.pollInterval, min = ref.min, max = ref.max; + interval = 5 * Math.log(this.reconnectAttempts + 1); + return Math.round(clamp(interval, min, max) * 1000); + }; + + ConnectionMonitor.prototype.reconnectIfStale = function() { + if (this.connectionIsStale()) { + ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + (this.getPollInterval()) + " ms, time disconnected = " + (secondsSince(this.disconnectedAt)) + " s, stale threshold = " + this.constructor.staleThreshold + " s"); + this.reconnectAttempts++; + if (this.disconnectedRecently()) { + return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect"); + } else { + ActionCable.log("ConnectionMonitor reopening"); + return this.connection.reopen(); + } + } + }; + + ConnectionMonitor.prototype.connectionIsStale = function() { + var ref; + return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold; + }; + + ConnectionMonitor.prototype.disconnectedRecently = function() { + return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold; + }; + + ConnectionMonitor.prototype.visibilityDidChange = function() { + if (document.visibilityState === "visible") { + return setTimeout((function(_this) { + return function() { + if (_this.connectionIsStale() || !_this.connection.isOpen()) { + ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState); + return _this.connection.reopen(); + } + }; + })(this), 200); + } + }; + + now = function() { + return new Date().getTime(); + }; + + secondsSince = function(time) { + return (now() - time) / 1000; + }; + + clamp = function(number, min, max) { + return Math.max(min, Math.min(max, number)); + }; + + return ConnectionMonitor; + + })(); + + }).call(this); + (function() { + var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol, + slice = [].slice, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols; + + supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++]; + + ActionCable.Connection = (function() { + Connection.reopenDelay = 500; + + function Connection(consumer) { + this.consumer = consumer; + this.open = bind(this.open, this); + this.subscriptions = this.consumer.subscriptions; + this.monitor = new ActionCable.ConnectionMonitor(this); + this.disconnected = true; + } + + Connection.prototype.send = function(data) { + if (this.isOpen()) { + this.webSocket.send(JSON.stringify(data)); + return true; + } else { + return false; + } + }; + + Connection.prototype.open = function() { + if (this.isActive()) { + ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState())); + return false; + } else { + ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols); + if (this.webSocket != null) { + this.uninstallEventHandlers(); + } + this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols); + this.installEventHandlers(); + this.monitor.start(); + return true; + } + }; + + Connection.prototype.close = function(arg) { + var allowReconnect, ref1; + allowReconnect = (arg != null ? arg : { + allowReconnect: true + }).allowReconnect; + if (!allowReconnect) { + this.monitor.stop(); + } + if (this.isActive()) { + return (ref1 = this.webSocket) != null ? ref1.close() : void 0; + } + }; + + Connection.prototype.reopen = function() { + var error; + ActionCable.log("Reopening WebSocket, current state is " + (this.getState())); + if (this.isActive()) { + try { + return this.close(); + } catch (error1) { + error = error1; + return ActionCable.log("Failed to reopen WebSocket", error); + } finally { + ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms"); + setTimeout(this.open, this.constructor.reopenDelay); + } + } else { + return this.open(); + } + }; + + Connection.prototype.getProtocol = function() { + var ref1; + return (ref1 = this.webSocket) != null ? ref1.protocol : void 0; + }; + + Connection.prototype.isOpen = function() { + return this.isState("open"); + }; + + Connection.prototype.isActive = function() { + return this.isState("open", "connecting"); + }; + + Connection.prototype.isProtocolSupported = function() { + var ref1; + return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0; + }; + + Connection.prototype.isState = function() { + var ref1, states; + states = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return ref1 = this.getState(), indexOf.call(states, ref1) >= 0; + }; + + Connection.prototype.getState = function() { + var ref1, state, value; + for (state in WebSocket) { + value = WebSocket[state]; + if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) { + return state.toLowerCase(); + } + } + return null; + }; + + Connection.prototype.installEventHandlers = function() { + var eventName, handler; + for (eventName in this.events) { + handler = this.events[eventName].bind(this); + this.webSocket["on" + eventName] = handler; + } + }; + + Connection.prototype.uninstallEventHandlers = function() { + var eventName; + for (eventName in this.events) { + this.webSocket["on" + eventName] = function() {}; + } + }; + + Connection.prototype.events = { + message: function(event) { + var identifier, message, ref1, type; + if (!this.isProtocolSupported()) { + return; + } + ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type; + switch (type) { + case message_types.welcome: + this.monitor.recordConnect(); + return this.subscriptions.reload(); + case message_types.ping: + return this.monitor.recordPing(); + case message_types.confirmation: + return this.subscriptions.notify(identifier, "connected"); + case message_types.rejection: + return this.subscriptions.reject(identifier); + default: + return this.subscriptions.notify(identifier, "received", message); + } + }, + open: function() { + ActionCable.log("WebSocket onopen event, using '" + (this.getProtocol()) + "' subprotocol"); + this.disconnected = false; + if (!this.isProtocolSupported()) { + ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting."); + return this.close({ + allowReconnect: false + }); + } + }, + close: function(event) { + ActionCable.log("WebSocket onclose event"); + if (this.disconnected) { + return; + } + this.disconnected = true; + this.monitor.recordDisconnect(); + return this.subscriptions.notifyAll("disconnected", { + willAttemptReconnect: this.monitor.isRunning() + }); + }, + error: function() { + return ActionCable.log("WebSocket onerror event"); + } + }; + + return Connection; + + })(); + + }).call(this); + (function() { + var slice = [].slice; + + ActionCable.Subscriptions = (function() { + function Subscriptions(consumer) { + this.consumer = consumer; + this.subscriptions = []; + } + + Subscriptions.prototype.create = function(channelName, mixin) { + var channel, params, subscription; + channel = channelName; + params = typeof channel === "object" ? channel : { + channel: channel + }; + subscription = new ActionCable.Subscription(this.consumer, params, mixin); + return this.add(subscription); + }; + + Subscriptions.prototype.add = function(subscription) { + this.subscriptions.push(subscription); + this.consumer.ensureActiveConnection(); + this.notify(subscription, "initialized"); + this.sendCommand(subscription, "subscribe"); + return subscription; + }; + + Subscriptions.prototype.remove = function(subscription) { + this.forget(subscription); + if (!this.findAll(subscription.identifier).length) { + this.sendCommand(subscription, "unsubscribe"); + } + return subscription; + }; + + Subscriptions.prototype.reject = function(identifier) { + var i, len, ref, results, subscription; + ref = this.findAll(identifier); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + this.forget(subscription); + this.notify(subscription, "rejected"); + results.push(subscription); + } + return results; + }; + + Subscriptions.prototype.forget = function(subscription) { + var s; + this.subscriptions = (function() { + var i, len, ref, results; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + if (s !== subscription) { + results.push(s); + } + } + return results; + }).call(this); + return subscription; + }; + + Subscriptions.prototype.findAll = function(identifier) { + var i, len, ref, results, s; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + if (s.identifier === identifier) { + results.push(s); + } + } + return results; + }; + + Subscriptions.prototype.reload = function() { + var i, len, ref, results, subscription; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + results.push(this.sendCommand(subscription, "subscribe")); + } + return results; + }; + + Subscriptions.prototype.notifyAll = function() { + var args, callbackName, i, len, ref, results, subscription; + callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + ref = this.subscriptions; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + subscription = ref[i]; + results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args)))); + } + return results; + }; + + Subscriptions.prototype.notify = function() { + var args, callbackName, i, len, results, subscription, subscriptions; + subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; + if (typeof subscription === "string") { + subscriptions = this.findAll(subscription); + } else { + subscriptions = [subscription]; + } + results = []; + for (i = 0, len = subscriptions.length; i < len; i++) { + subscription = subscriptions[i]; + results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0); + } + return results; + }; + + Subscriptions.prototype.sendCommand = function(subscription, command) { + var identifier; + identifier = subscription.identifier; + return this.consumer.send({ + command: command, + identifier: identifier + }); + }; + + return Subscriptions; + + })(); + + }).call(this); + (function() { + ActionCable.Subscription = (function() { + var extend; + + function Subscription(consumer, params, mixin) { + this.consumer = consumer; + if (params == null) { + params = {}; + } + this.identifier = JSON.stringify(params); + extend(this, mixin); + } + + Subscription.prototype.perform = function(action, data) { + if (data == null) { + data = {}; + } + data.action = action; + return this.send(data); + }; + + Subscription.prototype.send = function(data) { + return this.consumer.send({ + command: "message", + identifier: this.identifier, + data: JSON.stringify(data) + }); + }; + + Subscription.prototype.unsubscribe = function() { + return this.consumer.subscriptions.remove(this); + }; + + extend = function(object, properties) { + var key, value; + if (properties != null) { + for (key in properties) { + value = properties[key]; + object[key] = value; + } + } + return object; + }; + + return Subscription; + + })(); + + }).call(this); + (function() { + ActionCable.Consumer = (function() { + function Consumer(url) { + this.url = url; + this.subscriptions = new ActionCable.Subscriptions(this); + this.connection = new ActionCable.Connection(this); + } + + Consumer.prototype.send = function(data) { + return this.connection.send(data); + }; + + Consumer.prototype.connect = function() { + return this.connection.open(); + }; + + Consumer.prototype.disconnect = function() { + return this.connection.close({ + allowReconnect: false + }); + }; + + Consumer.prototype.ensureActiveConnection = function() { + if (!this.connection.isActive()) { + return this.connection.open(); + } + }; + + return Consumer; + + })(); + + }).call(this); + }).call(this); + + if (typeof module === "object" && module.exports) { + module.exports = ActionCable; + } else if (typeof define === "function" && define.amd) { + define(ActionCable); + } +}).call(this); +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// + + + + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// + +; diff --git a/spec/dummy/public/assets/application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js.gz b/spec/dummy/public/assets/application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js.gz new file mode 100644 index 0000000..630b145 Binary files /dev/null and b/spec/dummy/public/assets/application-e0385996f8c5e2c6755d0c15712a5c8a0543aa53ed856bba2f734c81d9e2ce8a.js.gz differ diff --git a/spec/dummy/public/assets/filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css b/spec/dummy/public/assets/filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css new file mode 100644 index 0000000..5e987a2 --- /dev/null +++ b/spec/dummy/public/assets/filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css @@ -0,0 +1,16 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + + + */ + diff --git a/spec/dummy/public/assets/filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css.gz b/spec/dummy/public/assets/filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css.gz new file mode 100644 index 0000000..e7eaa74 Binary files /dev/null and b/spec/dummy/public/assets/filestack_rails/application-af04b226fd7202dfc532ce7aedb95a0128277937e90d3b3a3d35e1cce9e16886.css.gz differ diff --git a/spec/dummy/public/assets/filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js b/spec/dummy/public/assets/filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js new file mode 100644 index 0000000..e95b6f1 --- /dev/null +++ b/spec/dummy/public/assets/filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js @@ -0,0 +1,14 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// + +; diff --git a/spec/dummy/public/assets/filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js.gz b/spec/dummy/public/assets/filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js.gz new file mode 100644 index 0000000..26b214f Binary files /dev/null and b/spec/dummy/public/assets/filestack_rails/application-d2d9c3e073309c3fb0def0c49409c35c24fcae7298534358135a1f5f7ed1dbb7.js.gz differ diff --git a/spec/dummy/test/controllers/hello_controller_test.rb b/spec/dummy/test/controllers/hello_controller_test.rb new file mode 100644 index 0000000..d8c9e9e --- /dev/null +++ b/spec/dummy/test/controllers/hello_controller_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class HelloControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get hello_index_url + assert_response :success + end + +end diff --git a/spec/dummy/test/fixtures/users.yml b/spec/dummy/test/fixtures/users.yml new file mode 100644 index 0000000..eeebb84 --- /dev/null +++ b/spec/dummy/test/fixtures/users.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + email: MyString + picture: MyString + +two: + name: MyString + email: MyString + picture: MyString diff --git a/spec/dummy/test/models/user_test.rb b/spec/dummy/test/models/user_test.rb new file mode 100644 index 0000000..82f61e0 --- /dev/null +++ b/spec/dummy/test/models/user_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/spec/features/rails_integration_spec.rb b/spec/features/rails_integration_spec.rb deleted file mode 100644 index 2f80d8d..0000000 --- a/spec/features/rails_integration_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'rails_helper' - -RSpec.feature 'Rails integration' do - - background do - visit '/' - end - - scenario 'adding the the javascript helper' do - script_tag = 'script[src="//api.filepicker.io/v2/filepicker.js"]' - expect(page).to have_css(script_tag, visible: false) - end - - scenario 'using the form helper' do - input_tag = 'input[data-fp-apikey="123filepickerapikey"]' \ - '[id="user_filepicker_url"][name="user[filepicker_url]"]' \ - '[type="filepicker"]' - expect(page).to have_css(input_tag) - end -end diff --git a/spec/filestack_rails_spec.rb b/spec/filestack_rails_spec.rb new file mode 100644 index 0000000..45a138f --- /dev/null +++ b/spec/filestack_rails_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe FilestackRails do + it "should equal true" do + expect(true).to eq(true) + end +end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index e6feec7..6593725 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -1,336 +1,32 @@ require 'rails_helper' -RSpec.describe FilepickerRails::ApplicationHelper do - - describe "#filepicker_js_include_tag" do - +RSpec.describe FilestackRails::ApplicationHelper do + include FilestackRails::ApplicationHelper + include ActionView::Helpers + describe '#filestack_js_include_tag' do it "be a script tag" do regex = %r{\A