mirror of
https://github.com/jcwimer/wrestlingApp
synced 2026-03-24 17:04:43 +00:00
Migrated from Sprockets to Propshaft.
This commit is contained in:
@@ -3,4 +3,5 @@
|
||||
- If the Gemfile changes, you need to rebuild the docker image: docker build -t wrestlingdev-dev -f deploy/rails-dev-Dockerfile.
|
||||
- Do not add unnecessary comments to the code where you remove things.
|
||||
- Cypress tests are created for js tests. They can be found in cypress-tests/cypress
|
||||
- Cypress tests can be run with docker: bash cypress-tests/run-cypress-tests.sh
|
||||
- Cypress tests can be run with docker: bash cypress-tests/run-cypress-tests.sh
|
||||
- Write as little code as possible. I do not want crazy non standard rails implementations.
|
||||
19
Gemfile
19
Gemfile
@@ -7,8 +7,9 @@ gem 'rails', '8.0.2'
|
||||
# Added in rails 7.1
|
||||
gem 'rails-html-sanitizer'
|
||||
|
||||
# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
|
||||
gem "sprockets-rails"
|
||||
# Asset Management: Propshaft for serving, Importmap for JavaScript
|
||||
gem "propshaft"
|
||||
gem "importmap-rails"
|
||||
|
||||
# Reduces boot times through caching; required in config/boot.rb
|
||||
gem "bootsnap", require: false
|
||||
@@ -17,17 +18,17 @@ gem "bootsnap", require: false
|
||||
# Use sqlite3 version compatible with Rails 8
|
||||
gem 'sqlite3', ">= 2.1", :group => :development
|
||||
|
||||
# Use Uglifier as compressor for JavaScript assets
|
||||
gem 'uglifier'
|
||||
# Use CoffeeScript for .js.coffee assets and views
|
||||
gem 'coffee-rails'
|
||||
# JavaScript and CSS related gems
|
||||
# Uglifier is not used with Propshaft by default
|
||||
# CoffeeScript (.js.coffee) files need to be converted to .js as Propshaft doesn't compile them
|
||||
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# gem 'therubyracer', platforms: :ruby
|
||||
|
||||
# Use jquery as the JavaScript library
|
||||
gem 'jquery-rails'
|
||||
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
||||
gem 'turbolinks'
|
||||
# Turbo for modern page interactions
|
||||
gem 'turbo-rails'
|
||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||
gem 'jbuilder'
|
||||
# bundle exec rake doc:rails generates the API under doc/api.
|
||||
@@ -90,5 +91,3 @@ group :development, :test do
|
||||
gem 'rails-controller-testing'
|
||||
end
|
||||
|
||||
gem 'font-awesome-sass'
|
||||
|
||||
|
||||
53
Gemfile.lock
53
Gemfile.lock
@@ -88,13 +88,6 @@ GEM
|
||||
bundler (>= 1.2.0, < 3)
|
||||
thor (~> 1.0)
|
||||
cancancan (3.6.1)
|
||||
coffee-rails (5.0.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 5.2.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.3)
|
||||
crass (1.0.6)
|
||||
@@ -104,17 +97,6 @@ GEM
|
||||
erubi (1.13.1)
|
||||
et-orbi (1.2.11)
|
||||
tzinfo
|
||||
execjs (2.10.0)
|
||||
ffi (1.17.2-aarch64-linux-gnu)
|
||||
ffi (1.17.2-aarch64-linux-musl)
|
||||
ffi (1.17.2-arm-linux-gnu)
|
||||
ffi (1.17.2-arm-linux-musl)
|
||||
ffi (1.17.2-arm64-darwin)
|
||||
ffi (1.17.2-x86_64-darwin)
|
||||
ffi (1.17.2-x86_64-linux-gnu)
|
||||
ffi (1.17.2-x86_64-linux-musl)
|
||||
font-awesome-sass (6.7.2)
|
||||
sassc (~> 2.0)
|
||||
fugit (1.11.1)
|
||||
et-orbi (~> 1, >= 1.2.11)
|
||||
raabro (~> 1.4)
|
||||
@@ -122,6 +104,10 @@ GEM
|
||||
activesupport (>= 6.1)
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
importmap-rails (2.1.0)
|
||||
actionpack (>= 6.0.0)
|
||||
activesupport (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
influxdb (0.8.1)
|
||||
influxdb-rails (1.0.3)
|
||||
influxdb (~> 0.6, >= 0.6.4)
|
||||
@@ -183,6 +169,11 @@ GEM
|
||||
pp (0.6.2)
|
||||
prettyprint
|
||||
prettyprint (0.2.0)
|
||||
propshaft (1.1.0)
|
||||
actionpack (>= 7.0.0)
|
||||
activesupport (>= 7.0.0)
|
||||
rack
|
||||
railties (>= 7.0.0)
|
||||
psych (5.2.6)
|
||||
date
|
||||
stringio
|
||||
@@ -244,8 +235,6 @@ GEM
|
||||
io-console (~> 0.5)
|
||||
round_robin_tournament (0.1.2)
|
||||
ruby2_keywords (0.0.5)
|
||||
sassc (2.4.0)
|
||||
ffi (~> 1.9)
|
||||
sdoc (2.6.1)
|
||||
rdoc (>= 5.0)
|
||||
securerandom (0.4.1)
|
||||
@@ -266,14 +255,6 @@ GEM
|
||||
railties (>= 7.1)
|
||||
thor (~> 1.3.1)
|
||||
spring (4.3.0)
|
||||
sprockets (4.2.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
logger
|
||||
rack (>= 2.2.4, < 4)
|
||||
sprockets-rails (3.5.2)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
sprockets (>= 3.0.0)
|
||||
sqlite3 (2.6.0-aarch64-linux-gnu)
|
||||
sqlite3 (2.6.0-aarch64-linux-musl)
|
||||
sqlite3 (2.6.0-arm-linux-gnu)
|
||||
@@ -285,15 +266,13 @@ GEM
|
||||
stringio (3.1.7)
|
||||
thor (1.3.2)
|
||||
timeout (0.4.3)
|
||||
turbolinks (5.2.1)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
turbo-rails (2.0.13)
|
||||
actionpack (>= 7.1.0)
|
||||
railties (>= 7.1.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2025.2)
|
||||
tzinfo (>= 1.0.0)
|
||||
uglifier (4.2.1)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
uniform_notifier (1.17.0)
|
||||
uri (1.0.3)
|
||||
useragent (0.16.11)
|
||||
@@ -320,14 +299,14 @@ DEPENDENCIES
|
||||
bullet
|
||||
bundler-audit
|
||||
cancancan
|
||||
coffee-rails
|
||||
daemons
|
||||
font-awesome-sass
|
||||
importmap-rails
|
||||
influxdb-rails
|
||||
jbuilder
|
||||
jquery-rails
|
||||
mocha
|
||||
mysql2
|
||||
propshaft
|
||||
puma
|
||||
rails (= 8.0.2)
|
||||
rails-controller-testing
|
||||
@@ -340,11 +319,9 @@ DEPENDENCIES
|
||||
solid_cache
|
||||
solid_queue
|
||||
spring
|
||||
sprockets-rails
|
||||
sqlite3 (>= 2.1)
|
||||
turbolinks
|
||||
turbo-rails
|
||||
tzinfo-data
|
||||
uglifier
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.2.0p0
|
||||
|
||||
13
README.md
13
README.md
@@ -174,4 +174,15 @@ SolidQueue plugin enabled in Puma
|
||||
|
||||
See `SOLID_QUEUE.md` for details about the job system configuration.
|
||||
|
||||
This project provides multiple ways to develop and deploy, with Docker being the primary method.
|
||||
This project provides multiple ways to develop and deploy, with Docker being the primary method.
|
||||
|
||||
# Sprockets to Propshaft Migration
|
||||
|
||||
- Propshaft will automatically include in its search paths the folders vendor/assets, lib/assets and app/assets of your project and of all the gems in your Gemfile. You can see all included files by using the reveal rake task: `rake assets:reveal`. When importing you'll use the relative path from this command.
|
||||
- All css files are imported via `app/assets/stylesheets/application.css`. This is imported on `app/views/layouts/application.html.erb`.
|
||||
- Bootstrap and fontawesome have been downloaded locally to `vendor/`
|
||||
- All js files are imported with a combination of "pinning" with `config/importmaps.rb` and `app/assets/javascript/application.js` and imported to `app/views/layouts/application.html.erb`
|
||||
- Jquery, bootstrap, datatables have been downloaded locally to `vendor/`
|
||||
- Turbo and action cable are gems and get pathed properly by propshaft.
|
||||
- development is "nobuild" with `config.assets.build_assets = false` in `config/environments/development.rb`
|
||||
- production needs to run rake assets:precompile. This is done in the `deploy/rails-prod-Dockerfile`.
|
||||
@@ -1,3 +1,11 @@
|
||||
//= link_tree ../images
|
||||
//= link_directory ../javascripts .js
|
||||
//= link_directory ../stylesheets .css
|
||||
|
||||
// Link all .js files from app/javascript for importmap, and vendor/javascript if needed directly
|
||||
//= link_tree ../../javascript .js
|
||||
//= link_tree ../../../vendor/assets/javascripts .js
|
||||
|
||||
// Explicitly link all .css files from app/assets/stylesheets and vendor/assets/stylesheets
|
||||
//= link_tree ../stylesheets .css
|
||||
//= link_tree ../../../vendor/assets/stylesheets .css
|
||||
|
||||
//= link_tree ../../../vendor/assets/webfonts
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,32 +1,27 @@
|
||||
// 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 vendor/assets/javascripts of plugins, if any, 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.
|
||||
//
|
||||
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
// Bootstrap 3.3.6 in vendor/assets/javascripts
|
||||
//= require bootstrap.min.js
|
||||
// Data Tables 1.10.6 in vendor/assets/javascripts
|
||||
//= require jquery.dataTables.min.js
|
||||
//= require turbolinks
|
||||
//
|
||||
//= require actioncable
|
||||
//= require_self
|
||||
//= require_tree .
|
||||
// Entry point for your JavaScript application
|
||||
|
||||
// Create the Action Cable consumer instance
|
||||
// These are pinned in config/importmap.rb
|
||||
import "@hotwired/turbo-rails";
|
||||
import { createConsumer } from "@rails/actioncable"; // Import createConsumer directly
|
||||
import "jquery";
|
||||
import "bootstrap";
|
||||
import "datatables.net";
|
||||
|
||||
// Your existing Action Cable consumer setup
|
||||
(function() {
|
||||
this.App || (this.App = {});
|
||||
|
||||
App.cable = ActionCable.createConsumer();
|
||||
|
||||
try {
|
||||
window.App || (window.App = {});
|
||||
window.App.cable = createConsumer(); // Use the imported createConsumer
|
||||
console.log('Action Cable Consumer Created via app/javascript/application.js');
|
||||
} catch (e) {
|
||||
console.error('Error creating ActionCable consumer:', e);
|
||||
console.error('ActionCable not loaded or createConsumer failed, App.cable not created.');
|
||||
}
|
||||
}).call(this);
|
||||
|
||||
console.log("Propshaft/Importmap application.js initialized with jQuery, Bootstrap, and DataTables.");
|
||||
|
||||
// If you have custom JavaScript files in app/javascript/ that were previously
|
||||
// handled by Sprockets `require_tree`, you'll need to import them here explicitly.
|
||||
// For example:
|
||||
// import "./my_custom_logic";
|
||||
@@ -1,4 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Admin controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
Place all the styles related to the matching controller here.
|
||||
They will automatically be included in application.css.
|
||||
*/
|
||||
@@ -1,35 +1,17 @@
|
||||
/*
|
||||
* 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 vendor/assets/stylesheets of plugins, if any, 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 styles
|
||||
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
||||
* file per style scope.
|
||||
*
|
||||
* For some reason this needs to be above bootstrap for the zindex of the main navbar to work.
|
||||
* With it lower, bootstraps css overrides it.
|
||||
*= require custom
|
||||
* Bootstrap 3.3.6 in vendor/assets/stylesheets
|
||||
*= require bootstrap.min.css
|
||||
*= require bootstrap-theme.min.css
|
||||
* Font Awesome 5.7.1 in vendor/assets/stylesheets/fontawesome
|
||||
*= require fontawesome/all
|
||||
*= require_tree .
|
||||
*= require_self
|
||||
*/
|
||||
/* relative pathing from /vender/assets/stylesheets = / */
|
||||
@import url("/bootstrap.min.css");
|
||||
@import url("/bootstrap-theme.min.css");
|
||||
@import url("/fontawesome/all.css");
|
||||
@import url("/custom.css");
|
||||
@import url("/scaffolds.css");
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
/* other properties */
|
||||
src: url("/webfonts/fa-brands-400.eot");
|
||||
src: url("/webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"),
|
||||
url("/webfonts/fa-brands-400.woff2") format("woff2"),
|
||||
url("/webfonts/fa-brands-400.woff") format("woff"),
|
||||
url("/webfonts/fa-brands-400.ttf") format("truetype"),
|
||||
url("/webfonts/fa-brands-400.svg#fontawesome") format("svg");
|
||||
}
|
||||
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
/* relative pathing from /vender/assets/stylesheets = / */
|
||||
src: url("/webfonts/fa-brands-400.eot");
|
||||
src: url("/webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"),
|
||||
url("/webfonts/fa-brands-400.woff2") format("woff2"),
|
||||
url("/webfonts/fa-brands-400.woff") format("woff"),
|
||||
url("/webfonts/fa-brands-400.ttf") format("truetype"),
|
||||
url("/webfonts/fa-brands-400.svg#fontawesome") format("svg");
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Matches controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Mats controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Schools controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the StaticPages controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the tournaments controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Weights controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Wrestlers controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -5,9 +5,8 @@
|
||||
<%= csrf_meta_tags %>
|
||||
<%= action_cable_meta_tag %>
|
||||
<title>WrestlingDev</title>
|
||||
<%= stylesheet_link_tag "application", media: "all",
|
||||
"data-turbolinks-track" => true %>
|
||||
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
|
||||
<%= stylesheet_link_tag "application", media: "all", "data-turbo-track": "reload" %>
|
||||
<%= javascript_importmap_tags %>
|
||||
<%= render 'layouts/cdn' %>
|
||||
<%= render 'layouts/shim' %>
|
||||
</head>
|
||||
@@ -22,8 +21,8 @@
|
||||
<% if Rails.env.production? %>
|
||||
<%= render 'layouts/analytics' %>
|
||||
<% end %>
|
||||
<%= stylesheet_link_tag "application" %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%= stylesheet_link_tag "application", media: "all", "data-turbo-track": "reload" %>
|
||||
<%= javascript_importmap_tags %>
|
||||
<%= csrf_meta_tags %>
|
||||
<%= render 'layouts/cdn' %>
|
||||
<%= render 'layouts/shim' %>
|
||||
|
||||
@@ -384,7 +384,7 @@ function initializeFromLocalStorage() {
|
||||
}
|
||||
|
||||
// ############### ACTION CABLE LIFECYCLE (Define Before Listeners) #############
|
||||
var matchSubscription = null; // Use var for safety with Turbolinks re-evaluation
|
||||
var matchSubscription = null; // Use var for safety with Turbo re-evaluation / page navigation
|
||||
|
||||
function cleanupSubscription() {
|
||||
if (matchSubscription) {
|
||||
@@ -484,8 +484,8 @@ function setupSubscription(matchId) {
|
||||
|
||||
// ############### EVENT LISTENERS (Define Last) #############
|
||||
|
||||
document.addEventListener("turbolinks:load", () => {
|
||||
console.log("Stats Event: turbolinks:load fired.");
|
||||
document.addEventListener("turbo:load", () => {
|
||||
console.log("Stats Event: turbo:load fired.");
|
||||
|
||||
// --- Check if we are actually on the match stats page ---
|
||||
const statsElementCheck = document.getElementById('match_w1_stat'); // Check for stats textarea
|
||||
@@ -507,12 +507,12 @@ document.addEventListener("turbolinks:load", () => {
|
||||
if (matchId) {
|
||||
setupSubscription(matchId);
|
||||
} else {
|
||||
console.warn("Stats Event: turbolinks:load - Could not determine match ID for AC setup.");
|
||||
console.warn("Stats Event: turbo:load - Could not determine match ID for AC setup.");
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("turbolinks:before-cache", () => {
|
||||
console.log("Event: turbolinks:before-cache fired. Cleaning up subscription.");
|
||||
document.addEventListener("turbo:before-cache", () => {
|
||||
console.log("Event: turbo:before-cache fired. Cleaning up subscription.");
|
||||
cleanupSubscription();
|
||||
});
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ function createTextInput(id, placeholder, label) {
|
||||
return container;
|
||||
}
|
||||
|
||||
// Initialize on both DOMContentLoaded and turbolinks:load
|
||||
// Initialize on both DOMContentLoaded and turbo:load
|
||||
document.addEventListener("DOMContentLoaded", initializeScoreFields);
|
||||
document.addEventListener("turbolinks:load", initializeScoreFields);
|
||||
document.addEventListener("turbo:load", initializeScoreFields);
|
||||
</script>
|
||||
|
||||
@@ -78,8 +78,8 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// ############### ACTION CABLE LIFECYCLE & SETUP #############
|
||||
var matchSubscription = null; // Use var for Turbolinks compatibility
|
||||
// ############### ACTION CABLE LIFECYCLE #############
|
||||
var matchSubscription = null; // Use var for handling Turbo page navigations
|
||||
|
||||
// Function to tear down the existing subscription
|
||||
function cleanupSubscription() {
|
||||
@@ -188,12 +188,12 @@ function setupSubscription(matchId) {
|
||||
);
|
||||
}
|
||||
|
||||
// ############### EVENT LISTENERS (Define Last) #############
|
||||
// ############### EVENT LISTENERS #############
|
||||
|
||||
document.addEventListener("turbolinks:load", () => {
|
||||
console.log("Spectator Event: turbolinks:load fired.");
|
||||
|
||||
// --- Check if we are actually on the spectator page ---
|
||||
document.addEventListener("turbo:load", () => {
|
||||
console.log("Spectator Event: turbo:load fired.");
|
||||
|
||||
// Check for necessary elements before proceeding
|
||||
const spectatorElementCheck = document.getElementById('w1-stats-display');
|
||||
if (!spectatorElementCheck) {
|
||||
console.log("Spectator Event: Not on spectator page, skipping AC setup.");
|
||||
@@ -201,18 +201,17 @@ document.addEventListener("turbolinks:load", () => {
|
||||
cleanupSubscription();
|
||||
return;
|
||||
}
|
||||
// --- End Check ---
|
||||
|
||||
|
||||
const matchId = <%= @match.id %>; // Get match ID from ERB
|
||||
if (matchId) {
|
||||
setupSubscription(matchId);
|
||||
} else {
|
||||
console.warn("Spectator Event: turbolinks:load - Could not determine match ID");
|
||||
console.warn("Spectator Event: turbo:load - Could not determine match ID");
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("turbolinks:before-cache", () => {
|
||||
console.log("Spectator Event: turbolinks:before-cache fired. Cleaning up subscription.");
|
||||
document.addEventListener("turbo:before-cache", () => {
|
||||
console.log("Spectator Event: turbo:before-cache fired. Cleaning up subscription.");
|
||||
cleanupSubscription();
|
||||
});
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ module Wrestling
|
||||
# Restored custom settings from original application.rb
|
||||
|
||||
# gzip assets
|
||||
config.middleware.use Rack::Deflater
|
||||
# config.middleware.use Rack::Deflater # Temporarily commented out for debugging asset 404s
|
||||
|
||||
config.active_job.queue_adapter = :solid_queue
|
||||
|
||||
|
||||
@@ -96,4 +96,7 @@ Rails.application.configure do
|
||||
|
||||
# Dump the schema after migrations
|
||||
config.active_record.dump_schema_after_migration = true
|
||||
|
||||
# Nobuild in development
|
||||
config.assets.build_assets = false
|
||||
end
|
||||
|
||||
24
config/importmap.rb
Normal file
24
config/importmap.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# Pin npm packages by running ./bin/importmap
|
||||
|
||||
pin "application", preload: true # Preloads app/javascript/application.js
|
||||
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
|
||||
pin "@rails/actioncable", to: "actioncable.esm.js" # For Action Cable
|
||||
|
||||
# Pin jQuery. jquery-rails should make "jquery.js" or "jquery.min.js" available.
|
||||
# If this doesn't work, you might need to copy jquery.js/jquery.min.js to vendor/javascript
|
||||
# and pin it directly, e.g., pin "jquery", to: "jquery.min.js"
|
||||
pin "jquery", to: "jquery.js"
|
||||
|
||||
# Pin Bootstrap and DataTables from vendor/assets/javascripts/
|
||||
pin "bootstrap", to: "bootstrap.min.js"
|
||||
pin "datatables.net", to: "jquery.dataTables.min.js" # Assuming this is how you want to import it
|
||||
|
||||
# If Bootstrap requires Popper.js, and you have it in vendor/assets/javascripts/
|
||||
# pin "@popperjs/core", to: "popper.min.js" # Or the actual filename if different
|
||||
|
||||
# Pin all files in app/javascript/controllers (if you use Stimulus, not explicitly requested yet)
|
||||
# pin_all_from "app/javascript/controllers", under: "controllers"
|
||||
|
||||
# Pin all JS files from app/javascript directory. They can be imported by their path relative to app/javascript.
|
||||
# For example, if you have app/javascript/custom/my_script.js, you can import it as import "custom/my_script";
|
||||
pin_all_from "app/javascript", under: "app/javascript"
|
||||
@@ -1,12 +0,0 @@
|
||||
# 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 the app/assets
|
||||
# folder are already added.
|
||||
# Rails.application.config.assets.precompile += %w[ admin.js admin.css ]
|
||||
@@ -71,7 +71,10 @@ describe('Pool to bracket setup', () => {
|
||||
|
||||
|
||||
// Go back to the tournament using the tournament navbar link
|
||||
// Doing intecept/wait because turbo is causing it to act like a spa
|
||||
cy.intercept('GET', /\/tournaments\/\d+$/).as('loadTournamentPageAfterLoop');
|
||||
cy.get('#tournament-navbar .navbar-brand').contains('Tournament Menu').click();
|
||||
cy.wait('@loadTournamentPageAfterLoop');
|
||||
cy.url().should('match', /\/tournaments\/\d+$/); // Check URL is /tournaments/ID
|
||||
cy.contains('Cypress Test Tournament - Pool to bracket').should('be.visible'); // Verify page content
|
||||
|
||||
@@ -93,6 +96,33 @@ describe('Pool to bracket setup', () => {
|
||||
cy.url().should('include', '/generate_matches');
|
||||
});
|
||||
|
||||
it('Should create a new mat.', () => {
|
||||
// Create boys weights
|
||||
// Listen for the confirmation popup and automatically confirm it
|
||||
cy.on('window:confirm', (text) => {
|
||||
return true; // Simulates clicking "OK"
|
||||
});
|
||||
|
||||
// Create Mat 1
|
||||
cy.get('body').then($body => {
|
||||
if (!$body.find('h3:contains("Mats")').length || !$body.find('a:contains("Mat 1")').length) {
|
||||
cy.contains('Director Links').first().click();
|
||||
cy.contains('New Mat').first().click();
|
||||
cy.url().should('include', '/mats/new');
|
||||
cy.get('input[name="mat[name]"]').type('1'); // Mat name is just '1'
|
||||
cy.get('input[type="submit"]').click({ multiple: true });
|
||||
cy.contains('a', 'Mat 1').should('be.visible');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should generate matches.', () => {
|
||||
// Generate Matches
|
||||
cy.contains('Director Links').first().click();
|
||||
cy.contains('Generate Brackets').first().click();
|
||||
cy.url().should('include', '/generate_matches');
|
||||
});
|
||||
|
||||
// This was creating a CORS error in Cypress. That seems to be a limitation of Cypress.
|
||||
// Putting this in a separate test to avoid the CORS error.
|
||||
it('Should wait for background jobs to finish.', () => {
|
||||
|
||||
@@ -185,7 +185,7 @@ describe('Matstats Real-time Updates', () => {
|
||||
cy.get('@newLoserScore').clear().type('0');
|
||||
|
||||
// Validation should be working - no error for valid major
|
||||
cy.get('#validation-alerts').should('not.exist').should('not.be.visible');
|
||||
cy.get('#validation-alerts').should('not.be.visible');
|
||||
|
||||
// Try an invalid combination
|
||||
cy.get('#match_win_type').select('Decision');
|
||||
|
||||
@@ -38,8 +38,11 @@ RUN bundle install --jobs 4
|
||||
# Copy site into place.
|
||||
ADD . /rails/
|
||||
|
||||
#Need temp secret keys to precompile assets
|
||||
#RUN RAILS_ENV=production bundle exec rake assets:precompile
|
||||
# Need a temporary secret key to precompile assets
|
||||
ENV WRESTLINGDEV_SECRET_KEY_BASE=077cdbef5c2ccf22543fb17a67339f234306b7fa2e1e4463d851c444c10a5611829a2290b253da78339427f131571fac9a42c83d960b2d25ecc10a4a0a7ce1a2
|
||||
# Precompile assets
|
||||
RUN RAILS_ENV=production bundle exec rake assets:clobber
|
||||
RUN RAILS_ENV=production bundle exec rake assets:precompile
|
||||
|
||||
#SSL
|
||||
RUN mkdir /ssl \
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,510 +0,0 @@
|
||||
(function(global, factory) {
|
||||
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
|
||||
factory(global.ActionCable = {}));
|
||||
})(this, (function(exports) {
|
||||
"use strict";
|
||||
var adapters = {
|
||||
logger: typeof console !== "undefined" ? console : undefined,
|
||||
WebSocket: typeof WebSocket !== "undefined" ? WebSocket : undefined
|
||||
};
|
||||
var logger = {
|
||||
log(...messages) {
|
||||
if (this.enabled) {
|
||||
messages.push(Date.now());
|
||||
adapters.logger.log("[ActionCable]", ...messages);
|
||||
}
|
||||
}
|
||||
};
|
||||
const now = () => (new Date).getTime();
|
||||
const secondsSince = time => (now() - time) / 1e3;
|
||||
class ConnectionMonitor {
|
||||
constructor(connection) {
|
||||
this.visibilityDidChange = this.visibilityDidChange.bind(this);
|
||||
this.connection = connection;
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
start() {
|
||||
if (!this.isRunning()) {
|
||||
this.startedAt = now();
|
||||
delete this.stoppedAt;
|
||||
this.startPolling();
|
||||
addEventListener("visibilitychange", this.visibilityDidChange);
|
||||
logger.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`);
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
if (this.isRunning()) {
|
||||
this.stoppedAt = now();
|
||||
this.stopPolling();
|
||||
removeEventListener("visibilitychange", this.visibilityDidChange);
|
||||
logger.log("ConnectionMonitor stopped");
|
||||
}
|
||||
}
|
||||
isRunning() {
|
||||
return this.startedAt && !this.stoppedAt;
|
||||
}
|
||||
recordMessage() {
|
||||
this.pingedAt = now();
|
||||
}
|
||||
recordConnect() {
|
||||
this.reconnectAttempts = 0;
|
||||
delete this.disconnectedAt;
|
||||
logger.log("ConnectionMonitor recorded connect");
|
||||
}
|
||||
recordDisconnect() {
|
||||
this.disconnectedAt = now();
|
||||
logger.log("ConnectionMonitor recorded disconnect");
|
||||
}
|
||||
startPolling() {
|
||||
this.stopPolling();
|
||||
this.poll();
|
||||
}
|
||||
stopPolling() {
|
||||
clearTimeout(this.pollTimeout);
|
||||
}
|
||||
poll() {
|
||||
this.pollTimeout = setTimeout((() => {
|
||||
this.reconnectIfStale();
|
||||
this.poll();
|
||||
}), this.getPollInterval());
|
||||
}
|
||||
getPollInterval() {
|
||||
const {staleThreshold: staleThreshold, reconnectionBackoffRate: reconnectionBackoffRate} = this.constructor;
|
||||
const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10));
|
||||
const jitterMax = this.reconnectAttempts === 0 ? 1 : reconnectionBackoffRate;
|
||||
const jitter = jitterMax * Math.random();
|
||||
return staleThreshold * 1e3 * backoff * (1 + jitter);
|
||||
}
|
||||
reconnectIfStale() {
|
||||
if (this.connectionIsStale()) {
|
||||
logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
|
||||
this.reconnectAttempts++;
|
||||
if (this.disconnectedRecently()) {
|
||||
logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
|
||||
} else {
|
||||
logger.log("ConnectionMonitor reopening");
|
||||
this.connection.reopen();
|
||||
}
|
||||
}
|
||||
}
|
||||
get refreshedAt() {
|
||||
return this.pingedAt ? this.pingedAt : this.startedAt;
|
||||
}
|
||||
connectionIsStale() {
|
||||
return secondsSince(this.refreshedAt) > this.constructor.staleThreshold;
|
||||
}
|
||||
disconnectedRecently() {
|
||||
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
|
||||
}
|
||||
visibilityDidChange() {
|
||||
if (document.visibilityState === "visible") {
|
||||
setTimeout((() => {
|
||||
if (this.connectionIsStale() || !this.connection.isOpen()) {
|
||||
logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`);
|
||||
this.connection.reopen();
|
||||
}
|
||||
}), 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnectionMonitor.staleThreshold = 6;
|
||||
ConnectionMonitor.reconnectionBackoffRate = .15;
|
||||
var INTERNAL = {
|
||||
message_types: {
|
||||
welcome: "welcome",
|
||||
disconnect: "disconnect",
|
||||
ping: "ping",
|
||||
confirmation: "confirm_subscription",
|
||||
rejection: "reject_subscription"
|
||||
},
|
||||
disconnect_reasons: {
|
||||
unauthorized: "unauthorized",
|
||||
invalid_request: "invalid_request",
|
||||
server_restart: "server_restart",
|
||||
remote: "remote"
|
||||
},
|
||||
default_mount_path: "/cable",
|
||||
protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
|
||||
};
|
||||
const {message_types: message_types, protocols: protocols} = INTERNAL;
|
||||
const supportedProtocols = protocols.slice(0, protocols.length - 1);
|
||||
const indexOf = [].indexOf;
|
||||
class Connection {
|
||||
constructor(consumer) {
|
||||
this.open = this.open.bind(this);
|
||||
this.consumer = consumer;
|
||||
this.subscriptions = this.consumer.subscriptions;
|
||||
this.monitor = new ConnectionMonitor(this);
|
||||
this.disconnected = true;
|
||||
}
|
||||
send(data) {
|
||||
if (this.isOpen()) {
|
||||
this.webSocket.send(JSON.stringify(data));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
open() {
|
||||
if (this.isActive()) {
|
||||
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
|
||||
return false;
|
||||
} else {
|
||||
const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
|
||||
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
|
||||
if (this.webSocket) {
|
||||
this.uninstallEventHandlers();
|
||||
}
|
||||
this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
|
||||
this.installEventHandlers();
|
||||
this.monitor.start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
close({allowReconnect: allowReconnect} = {
|
||||
allowReconnect: true
|
||||
}) {
|
||||
if (!allowReconnect) {
|
||||
this.monitor.stop();
|
||||
}
|
||||
if (this.isOpen()) {
|
||||
return this.webSocket.close();
|
||||
}
|
||||
}
|
||||
reopen() {
|
||||
logger.log(`Reopening WebSocket, current state is ${this.getState()}`);
|
||||
if (this.isActive()) {
|
||||
try {
|
||||
return this.close();
|
||||
} catch (error) {
|
||||
logger.log("Failed to reopen WebSocket", error);
|
||||
} finally {
|
||||
logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
|
||||
setTimeout(this.open, this.constructor.reopenDelay);
|
||||
}
|
||||
} else {
|
||||
return this.open();
|
||||
}
|
||||
}
|
||||
getProtocol() {
|
||||
if (this.webSocket) {
|
||||
return this.webSocket.protocol;
|
||||
}
|
||||
}
|
||||
isOpen() {
|
||||
return this.isState("open");
|
||||
}
|
||||
isActive() {
|
||||
return this.isState("open", "connecting");
|
||||
}
|
||||
triedToReconnect() {
|
||||
return this.monitor.reconnectAttempts > 0;
|
||||
}
|
||||
isProtocolSupported() {
|
||||
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
|
||||
}
|
||||
isState(...states) {
|
||||
return indexOf.call(states, this.getState()) >= 0;
|
||||
}
|
||||
getState() {
|
||||
if (this.webSocket) {
|
||||
for (let state in adapters.WebSocket) {
|
||||
if (adapters.WebSocket[state] === this.webSocket.readyState) {
|
||||
return state.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
installEventHandlers() {
|
||||
for (let eventName in this.events) {
|
||||
const handler = this.events[eventName].bind(this);
|
||||
this.webSocket[`on${eventName}`] = handler;
|
||||
}
|
||||
}
|
||||
uninstallEventHandlers() {
|
||||
for (let eventName in this.events) {
|
||||
this.webSocket[`on${eventName}`] = function() {};
|
||||
}
|
||||
}
|
||||
}
|
||||
Connection.reopenDelay = 500;
|
||||
Connection.prototype.events = {
|
||||
message(event) {
|
||||
if (!this.isProtocolSupported()) {
|
||||
return;
|
||||
}
|
||||
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
||||
this.monitor.recordMessage();
|
||||
switch (type) {
|
||||
case message_types.welcome:
|
||||
if (this.triedToReconnect()) {
|
||||
this.reconnectAttempted = true;
|
||||
}
|
||||
this.monitor.recordConnect();
|
||||
return this.subscriptions.reload();
|
||||
|
||||
case message_types.disconnect:
|
||||
logger.log(`Disconnecting. Reason: ${reason}`);
|
||||
return this.close({
|
||||
allowReconnect: reconnect
|
||||
});
|
||||
|
||||
case message_types.ping:
|
||||
return null;
|
||||
|
||||
case message_types.confirmation:
|
||||
this.subscriptions.confirmSubscription(identifier);
|
||||
if (this.reconnectAttempted) {
|
||||
this.reconnectAttempted = false;
|
||||
return this.subscriptions.notify(identifier, "connected", {
|
||||
reconnected: true
|
||||
});
|
||||
} else {
|
||||
return this.subscriptions.notify(identifier, "connected", {
|
||||
reconnected: false
|
||||
});
|
||||
}
|
||||
|
||||
case message_types.rejection:
|
||||
return this.subscriptions.reject(identifier);
|
||||
|
||||
default:
|
||||
return this.subscriptions.notify(identifier, "received", message);
|
||||
}
|
||||
},
|
||||
open() {
|
||||
logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`);
|
||||
this.disconnected = false;
|
||||
if (!this.isProtocolSupported()) {
|
||||
logger.log("Protocol is unsupported. Stopping monitor and disconnecting.");
|
||||
return this.close({
|
||||
allowReconnect: false
|
||||
});
|
||||
}
|
||||
},
|
||||
close(event) {
|
||||
logger.log("WebSocket onclose event");
|
||||
if (this.disconnected) {
|
||||
return;
|
||||
}
|
||||
this.disconnected = true;
|
||||
this.monitor.recordDisconnect();
|
||||
return this.subscriptions.notifyAll("disconnected", {
|
||||
willAttemptReconnect: this.monitor.isRunning()
|
||||
});
|
||||
},
|
||||
error() {
|
||||
logger.log("WebSocket onerror event");
|
||||
}
|
||||
};
|
||||
const extend = function(object, properties) {
|
||||
if (properties != null) {
|
||||
for (let key in properties) {
|
||||
const value = properties[key];
|
||||
object[key] = value;
|
||||
}
|
||||
}
|
||||
return object;
|
||||
};
|
||||
class Subscription {
|
||||
constructor(consumer, params = {}, mixin) {
|
||||
this.consumer = consumer;
|
||||
this.identifier = JSON.stringify(params);
|
||||
extend(this, mixin);
|
||||
}
|
||||
perform(action, data = {}) {
|
||||
data.action = action;
|
||||
return this.send(data);
|
||||
}
|
||||
send(data) {
|
||||
return this.consumer.send({
|
||||
command: "message",
|
||||
identifier: this.identifier,
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
unsubscribe() {
|
||||
return this.consumer.subscriptions.remove(this);
|
||||
}
|
||||
}
|
||||
class SubscriptionGuarantor {
|
||||
constructor(subscriptions) {
|
||||
this.subscriptions = subscriptions;
|
||||
this.pendingSubscriptions = [];
|
||||
}
|
||||
guarantee(subscription) {
|
||||
if (this.pendingSubscriptions.indexOf(subscription) == -1) {
|
||||
logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
|
||||
this.pendingSubscriptions.push(subscription);
|
||||
} else {
|
||||
logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
|
||||
}
|
||||
this.startGuaranteeing();
|
||||
}
|
||||
forget(subscription) {
|
||||
logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
|
||||
this.pendingSubscriptions = this.pendingSubscriptions.filter((s => s !== subscription));
|
||||
}
|
||||
startGuaranteeing() {
|
||||
this.stopGuaranteeing();
|
||||
this.retrySubscribing();
|
||||
}
|
||||
stopGuaranteeing() {
|
||||
clearTimeout(this.retryTimeout);
|
||||
}
|
||||
retrySubscribing() {
|
||||
this.retryTimeout = setTimeout((() => {
|
||||
if (this.subscriptions && typeof this.subscriptions.subscribe === "function") {
|
||||
this.pendingSubscriptions.map((subscription => {
|
||||
logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
|
||||
this.subscriptions.subscribe(subscription);
|
||||
}));
|
||||
}
|
||||
}), 500);
|
||||
}
|
||||
}
|
||||
class Subscriptions {
|
||||
constructor(consumer) {
|
||||
this.consumer = consumer;
|
||||
this.guarantor = new SubscriptionGuarantor(this);
|
||||
this.subscriptions = [];
|
||||
}
|
||||
create(channelName, mixin) {
|
||||
const channel = channelName;
|
||||
const params = typeof channel === "object" ? channel : {
|
||||
channel: channel
|
||||
};
|
||||
const subscription = new Subscription(this.consumer, params, mixin);
|
||||
return this.add(subscription);
|
||||
}
|
||||
add(subscription) {
|
||||
this.subscriptions.push(subscription);
|
||||
this.consumer.ensureActiveConnection();
|
||||
this.notify(subscription, "initialized");
|
||||
this.subscribe(subscription);
|
||||
return subscription;
|
||||
}
|
||||
remove(subscription) {
|
||||
this.forget(subscription);
|
||||
if (!this.findAll(subscription.identifier).length) {
|
||||
this.sendCommand(subscription, "unsubscribe");
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
reject(identifier) {
|
||||
return this.findAll(identifier).map((subscription => {
|
||||
this.forget(subscription);
|
||||
this.notify(subscription, "rejected");
|
||||
return subscription;
|
||||
}));
|
||||
}
|
||||
forget(subscription) {
|
||||
this.guarantor.forget(subscription);
|
||||
this.subscriptions = this.subscriptions.filter((s => s !== subscription));
|
||||
return subscription;
|
||||
}
|
||||
findAll(identifier) {
|
||||
return this.subscriptions.filter((s => s.identifier === identifier));
|
||||
}
|
||||
reload() {
|
||||
return this.subscriptions.map((subscription => this.subscribe(subscription)));
|
||||
}
|
||||
notifyAll(callbackName, ...args) {
|
||||
return this.subscriptions.map((subscription => this.notify(subscription, callbackName, ...args)));
|
||||
}
|
||||
notify(subscription, callbackName, ...args) {
|
||||
let subscriptions;
|
||||
if (typeof subscription === "string") {
|
||||
subscriptions = this.findAll(subscription);
|
||||
} else {
|
||||
subscriptions = [ subscription ];
|
||||
}
|
||||
return subscriptions.map((subscription => typeof subscription[callbackName] === "function" ? subscription[callbackName](...args) : undefined));
|
||||
}
|
||||
subscribe(subscription) {
|
||||
if (this.sendCommand(subscription, "subscribe")) {
|
||||
this.guarantor.guarantee(subscription);
|
||||
}
|
||||
}
|
||||
confirmSubscription(identifier) {
|
||||
logger.log(`Subscription confirmed ${identifier}`);
|
||||
this.findAll(identifier).map((subscription => this.guarantor.forget(subscription)));
|
||||
}
|
||||
sendCommand(subscription, command) {
|
||||
const {identifier: identifier} = subscription;
|
||||
return this.consumer.send({
|
||||
command: command,
|
||||
identifier: identifier
|
||||
});
|
||||
}
|
||||
}
|
||||
class Consumer {
|
||||
constructor(url) {
|
||||
this._url = url;
|
||||
this.subscriptions = new Subscriptions(this);
|
||||
this.connection = new Connection(this);
|
||||
this.subprotocols = [];
|
||||
}
|
||||
get url() {
|
||||
return createWebSocketURL(this._url);
|
||||
}
|
||||
send(data) {
|
||||
return this.connection.send(data);
|
||||
}
|
||||
connect() {
|
||||
return this.connection.open();
|
||||
}
|
||||
disconnect() {
|
||||
return this.connection.close({
|
||||
allowReconnect: false
|
||||
});
|
||||
}
|
||||
ensureActiveConnection() {
|
||||
if (!this.connection.isActive()) {
|
||||
return this.connection.open();
|
||||
}
|
||||
}
|
||||
addSubProtocol(subprotocol) {
|
||||
this.subprotocols = [ ...this.subprotocols, subprotocol ];
|
||||
}
|
||||
}
|
||||
function createWebSocketURL(url) {
|
||||
if (typeof url === "function") {
|
||||
url = url();
|
||||
}
|
||||
if (url && !/^wss?:/i.test(url)) {
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.href = a.href;
|
||||
a.protocol = a.protocol.replace("http", "ws");
|
||||
return a.href;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) {
|
||||
return new Consumer(url);
|
||||
}
|
||||
function getConfig(name) {
|
||||
const element = document.head.querySelector(`meta[name='action-cable-${name}']`);
|
||||
if (element) {
|
||||
return element.getAttribute("content");
|
||||
}
|
||||
}
|
||||
exports.Connection = Connection;
|
||||
exports.ConnectionMonitor = ConnectionMonitor;
|
||||
exports.Consumer = Consumer;
|
||||
exports.INTERNAL = INTERNAL;
|
||||
exports.Subscription = Subscription;
|
||||
exports.SubscriptionGuarantor = SubscriptionGuarantor;
|
||||
exports.Subscriptions = Subscriptions;
|
||||
exports.adapters = adapters;
|
||||
exports.createConsumer = createConsumer;
|
||||
exports.createWebSocketURL = createWebSocketURL;
|
||||
exports.getConfig = getConfig;
|
||||
exports.logger = logger;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
}));
|
||||
Binary file not shown.
@@ -1,512 +0,0 @@
|
||||
var adapters = {
|
||||
logger: typeof console !== "undefined" ? console : undefined,
|
||||
WebSocket: typeof WebSocket !== "undefined" ? WebSocket : undefined
|
||||
};
|
||||
|
||||
var logger = {
|
||||
log(...messages) {
|
||||
if (this.enabled) {
|
||||
messages.push(Date.now());
|
||||
adapters.logger.log("[ActionCable]", ...messages);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const now = () => (new Date).getTime();
|
||||
|
||||
const secondsSince = time => (now() - time) / 1e3;
|
||||
|
||||
class ConnectionMonitor {
|
||||
constructor(connection) {
|
||||
this.visibilityDidChange = this.visibilityDidChange.bind(this);
|
||||
this.connection = connection;
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
start() {
|
||||
if (!this.isRunning()) {
|
||||
this.startedAt = now();
|
||||
delete this.stoppedAt;
|
||||
this.startPolling();
|
||||
addEventListener("visibilitychange", this.visibilityDidChange);
|
||||
logger.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`);
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
if (this.isRunning()) {
|
||||
this.stoppedAt = now();
|
||||
this.stopPolling();
|
||||
removeEventListener("visibilitychange", this.visibilityDidChange);
|
||||
logger.log("ConnectionMonitor stopped");
|
||||
}
|
||||
}
|
||||
isRunning() {
|
||||
return this.startedAt && !this.stoppedAt;
|
||||
}
|
||||
recordMessage() {
|
||||
this.pingedAt = now();
|
||||
}
|
||||
recordConnect() {
|
||||
this.reconnectAttempts = 0;
|
||||
delete this.disconnectedAt;
|
||||
logger.log("ConnectionMonitor recorded connect");
|
||||
}
|
||||
recordDisconnect() {
|
||||
this.disconnectedAt = now();
|
||||
logger.log("ConnectionMonitor recorded disconnect");
|
||||
}
|
||||
startPolling() {
|
||||
this.stopPolling();
|
||||
this.poll();
|
||||
}
|
||||
stopPolling() {
|
||||
clearTimeout(this.pollTimeout);
|
||||
}
|
||||
poll() {
|
||||
this.pollTimeout = setTimeout((() => {
|
||||
this.reconnectIfStale();
|
||||
this.poll();
|
||||
}), this.getPollInterval());
|
||||
}
|
||||
getPollInterval() {
|
||||
const {staleThreshold: staleThreshold, reconnectionBackoffRate: reconnectionBackoffRate} = this.constructor;
|
||||
const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10));
|
||||
const jitterMax = this.reconnectAttempts === 0 ? 1 : reconnectionBackoffRate;
|
||||
const jitter = jitterMax * Math.random();
|
||||
return staleThreshold * 1e3 * backoff * (1 + jitter);
|
||||
}
|
||||
reconnectIfStale() {
|
||||
if (this.connectionIsStale()) {
|
||||
logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
|
||||
this.reconnectAttempts++;
|
||||
if (this.disconnectedRecently()) {
|
||||
logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
|
||||
} else {
|
||||
logger.log("ConnectionMonitor reopening");
|
||||
this.connection.reopen();
|
||||
}
|
||||
}
|
||||
}
|
||||
get refreshedAt() {
|
||||
return this.pingedAt ? this.pingedAt : this.startedAt;
|
||||
}
|
||||
connectionIsStale() {
|
||||
return secondsSince(this.refreshedAt) > this.constructor.staleThreshold;
|
||||
}
|
||||
disconnectedRecently() {
|
||||
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
|
||||
}
|
||||
visibilityDidChange() {
|
||||
if (document.visibilityState === "visible") {
|
||||
setTimeout((() => {
|
||||
if (this.connectionIsStale() || !this.connection.isOpen()) {
|
||||
logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`);
|
||||
this.connection.reopen();
|
||||
}
|
||||
}), 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionMonitor.staleThreshold = 6;
|
||||
|
||||
ConnectionMonitor.reconnectionBackoffRate = .15;
|
||||
|
||||
var INTERNAL = {
|
||||
message_types: {
|
||||
welcome: "welcome",
|
||||
disconnect: "disconnect",
|
||||
ping: "ping",
|
||||
confirmation: "confirm_subscription",
|
||||
rejection: "reject_subscription"
|
||||
},
|
||||
disconnect_reasons: {
|
||||
unauthorized: "unauthorized",
|
||||
invalid_request: "invalid_request",
|
||||
server_restart: "server_restart",
|
||||
remote: "remote"
|
||||
},
|
||||
default_mount_path: "/cable",
|
||||
protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
|
||||
};
|
||||
|
||||
const {message_types: message_types, protocols: protocols} = INTERNAL;
|
||||
|
||||
const supportedProtocols = protocols.slice(0, protocols.length - 1);
|
||||
|
||||
const indexOf = [].indexOf;
|
||||
|
||||
class Connection {
|
||||
constructor(consumer) {
|
||||
this.open = this.open.bind(this);
|
||||
this.consumer = consumer;
|
||||
this.subscriptions = this.consumer.subscriptions;
|
||||
this.monitor = new ConnectionMonitor(this);
|
||||
this.disconnected = true;
|
||||
}
|
||||
send(data) {
|
||||
if (this.isOpen()) {
|
||||
this.webSocket.send(JSON.stringify(data));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
open() {
|
||||
if (this.isActive()) {
|
||||
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
|
||||
return false;
|
||||
} else {
|
||||
const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
|
||||
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
|
||||
if (this.webSocket) {
|
||||
this.uninstallEventHandlers();
|
||||
}
|
||||
this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
|
||||
this.installEventHandlers();
|
||||
this.monitor.start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
close({allowReconnect: allowReconnect} = {
|
||||
allowReconnect: true
|
||||
}) {
|
||||
if (!allowReconnect) {
|
||||
this.monitor.stop();
|
||||
}
|
||||
if (this.isOpen()) {
|
||||
return this.webSocket.close();
|
||||
}
|
||||
}
|
||||
reopen() {
|
||||
logger.log(`Reopening WebSocket, current state is ${this.getState()}`);
|
||||
if (this.isActive()) {
|
||||
try {
|
||||
return this.close();
|
||||
} catch (error) {
|
||||
logger.log("Failed to reopen WebSocket", error);
|
||||
} finally {
|
||||
logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
|
||||
setTimeout(this.open, this.constructor.reopenDelay);
|
||||
}
|
||||
} else {
|
||||
return this.open();
|
||||
}
|
||||
}
|
||||
getProtocol() {
|
||||
if (this.webSocket) {
|
||||
return this.webSocket.protocol;
|
||||
}
|
||||
}
|
||||
isOpen() {
|
||||
return this.isState("open");
|
||||
}
|
||||
isActive() {
|
||||
return this.isState("open", "connecting");
|
||||
}
|
||||
triedToReconnect() {
|
||||
return this.monitor.reconnectAttempts > 0;
|
||||
}
|
||||
isProtocolSupported() {
|
||||
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
|
||||
}
|
||||
isState(...states) {
|
||||
return indexOf.call(states, this.getState()) >= 0;
|
||||
}
|
||||
getState() {
|
||||
if (this.webSocket) {
|
||||
for (let state in adapters.WebSocket) {
|
||||
if (adapters.WebSocket[state] === this.webSocket.readyState) {
|
||||
return state.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
installEventHandlers() {
|
||||
for (let eventName in this.events) {
|
||||
const handler = this.events[eventName].bind(this);
|
||||
this.webSocket[`on${eventName}`] = handler;
|
||||
}
|
||||
}
|
||||
uninstallEventHandlers() {
|
||||
for (let eventName in this.events) {
|
||||
this.webSocket[`on${eventName}`] = function() {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connection.reopenDelay = 500;
|
||||
|
||||
Connection.prototype.events = {
|
||||
message(event) {
|
||||
if (!this.isProtocolSupported()) {
|
||||
return;
|
||||
}
|
||||
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
||||
this.monitor.recordMessage();
|
||||
switch (type) {
|
||||
case message_types.welcome:
|
||||
if (this.triedToReconnect()) {
|
||||
this.reconnectAttempted = true;
|
||||
}
|
||||
this.monitor.recordConnect();
|
||||
return this.subscriptions.reload();
|
||||
|
||||
case message_types.disconnect:
|
||||
logger.log(`Disconnecting. Reason: ${reason}`);
|
||||
return this.close({
|
||||
allowReconnect: reconnect
|
||||
});
|
||||
|
||||
case message_types.ping:
|
||||
return null;
|
||||
|
||||
case message_types.confirmation:
|
||||
this.subscriptions.confirmSubscription(identifier);
|
||||
if (this.reconnectAttempted) {
|
||||
this.reconnectAttempted = false;
|
||||
return this.subscriptions.notify(identifier, "connected", {
|
||||
reconnected: true
|
||||
});
|
||||
} else {
|
||||
return this.subscriptions.notify(identifier, "connected", {
|
||||
reconnected: false
|
||||
});
|
||||
}
|
||||
|
||||
case message_types.rejection:
|
||||
return this.subscriptions.reject(identifier);
|
||||
|
||||
default:
|
||||
return this.subscriptions.notify(identifier, "received", message);
|
||||
}
|
||||
},
|
||||
open() {
|
||||
logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`);
|
||||
this.disconnected = false;
|
||||
if (!this.isProtocolSupported()) {
|
||||
logger.log("Protocol is unsupported. Stopping monitor and disconnecting.");
|
||||
return this.close({
|
||||
allowReconnect: false
|
||||
});
|
||||
}
|
||||
},
|
||||
close(event) {
|
||||
logger.log("WebSocket onclose event");
|
||||
if (this.disconnected) {
|
||||
return;
|
||||
}
|
||||
this.disconnected = true;
|
||||
this.monitor.recordDisconnect();
|
||||
return this.subscriptions.notifyAll("disconnected", {
|
||||
willAttemptReconnect: this.monitor.isRunning()
|
||||
});
|
||||
},
|
||||
error() {
|
||||
logger.log("WebSocket onerror event");
|
||||
}
|
||||
};
|
||||
|
||||
const extend = function(object, properties) {
|
||||
if (properties != null) {
|
||||
for (let key in properties) {
|
||||
const value = properties[key];
|
||||
object[key] = value;
|
||||
}
|
||||
}
|
||||
return object;
|
||||
};
|
||||
|
||||
class Subscription {
|
||||
constructor(consumer, params = {}, mixin) {
|
||||
this.consumer = consumer;
|
||||
this.identifier = JSON.stringify(params);
|
||||
extend(this, mixin);
|
||||
}
|
||||
perform(action, data = {}) {
|
||||
data.action = action;
|
||||
return this.send(data);
|
||||
}
|
||||
send(data) {
|
||||
return this.consumer.send({
|
||||
command: "message",
|
||||
identifier: this.identifier,
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
unsubscribe() {
|
||||
return this.consumer.subscriptions.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionGuarantor {
|
||||
constructor(subscriptions) {
|
||||
this.subscriptions = subscriptions;
|
||||
this.pendingSubscriptions = [];
|
||||
}
|
||||
guarantee(subscription) {
|
||||
if (this.pendingSubscriptions.indexOf(subscription) == -1) {
|
||||
logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
|
||||
this.pendingSubscriptions.push(subscription);
|
||||
} else {
|
||||
logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
|
||||
}
|
||||
this.startGuaranteeing();
|
||||
}
|
||||
forget(subscription) {
|
||||
logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
|
||||
this.pendingSubscriptions = this.pendingSubscriptions.filter((s => s !== subscription));
|
||||
}
|
||||
startGuaranteeing() {
|
||||
this.stopGuaranteeing();
|
||||
this.retrySubscribing();
|
||||
}
|
||||
stopGuaranteeing() {
|
||||
clearTimeout(this.retryTimeout);
|
||||
}
|
||||
retrySubscribing() {
|
||||
this.retryTimeout = setTimeout((() => {
|
||||
if (this.subscriptions && typeof this.subscriptions.subscribe === "function") {
|
||||
this.pendingSubscriptions.map((subscription => {
|
||||
logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
|
||||
this.subscriptions.subscribe(subscription);
|
||||
}));
|
||||
}
|
||||
}), 500);
|
||||
}
|
||||
}
|
||||
|
||||
class Subscriptions {
|
||||
constructor(consumer) {
|
||||
this.consumer = consumer;
|
||||
this.guarantor = new SubscriptionGuarantor(this);
|
||||
this.subscriptions = [];
|
||||
}
|
||||
create(channelName, mixin) {
|
||||
const channel = channelName;
|
||||
const params = typeof channel === "object" ? channel : {
|
||||
channel: channel
|
||||
};
|
||||
const subscription = new Subscription(this.consumer, params, mixin);
|
||||
return this.add(subscription);
|
||||
}
|
||||
add(subscription) {
|
||||
this.subscriptions.push(subscription);
|
||||
this.consumer.ensureActiveConnection();
|
||||
this.notify(subscription, "initialized");
|
||||
this.subscribe(subscription);
|
||||
return subscription;
|
||||
}
|
||||
remove(subscription) {
|
||||
this.forget(subscription);
|
||||
if (!this.findAll(subscription.identifier).length) {
|
||||
this.sendCommand(subscription, "unsubscribe");
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
reject(identifier) {
|
||||
return this.findAll(identifier).map((subscription => {
|
||||
this.forget(subscription);
|
||||
this.notify(subscription, "rejected");
|
||||
return subscription;
|
||||
}));
|
||||
}
|
||||
forget(subscription) {
|
||||
this.guarantor.forget(subscription);
|
||||
this.subscriptions = this.subscriptions.filter((s => s !== subscription));
|
||||
return subscription;
|
||||
}
|
||||
findAll(identifier) {
|
||||
return this.subscriptions.filter((s => s.identifier === identifier));
|
||||
}
|
||||
reload() {
|
||||
return this.subscriptions.map((subscription => this.subscribe(subscription)));
|
||||
}
|
||||
notifyAll(callbackName, ...args) {
|
||||
return this.subscriptions.map((subscription => this.notify(subscription, callbackName, ...args)));
|
||||
}
|
||||
notify(subscription, callbackName, ...args) {
|
||||
let subscriptions;
|
||||
if (typeof subscription === "string") {
|
||||
subscriptions = this.findAll(subscription);
|
||||
} else {
|
||||
subscriptions = [ subscription ];
|
||||
}
|
||||
return subscriptions.map((subscription => typeof subscription[callbackName] === "function" ? subscription[callbackName](...args) : undefined));
|
||||
}
|
||||
subscribe(subscription) {
|
||||
if (this.sendCommand(subscription, "subscribe")) {
|
||||
this.guarantor.guarantee(subscription);
|
||||
}
|
||||
}
|
||||
confirmSubscription(identifier) {
|
||||
logger.log(`Subscription confirmed ${identifier}`);
|
||||
this.findAll(identifier).map((subscription => this.guarantor.forget(subscription)));
|
||||
}
|
||||
sendCommand(subscription, command) {
|
||||
const {identifier: identifier} = subscription;
|
||||
return this.consumer.send({
|
||||
command: command,
|
||||
identifier: identifier
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer {
|
||||
constructor(url) {
|
||||
this._url = url;
|
||||
this.subscriptions = new Subscriptions(this);
|
||||
this.connection = new Connection(this);
|
||||
this.subprotocols = [];
|
||||
}
|
||||
get url() {
|
||||
return createWebSocketURL(this._url);
|
||||
}
|
||||
send(data) {
|
||||
return this.connection.send(data);
|
||||
}
|
||||
connect() {
|
||||
return this.connection.open();
|
||||
}
|
||||
disconnect() {
|
||||
return this.connection.close({
|
||||
allowReconnect: false
|
||||
});
|
||||
}
|
||||
ensureActiveConnection() {
|
||||
if (!this.connection.isActive()) {
|
||||
return this.connection.open();
|
||||
}
|
||||
}
|
||||
addSubProtocol(subprotocol) {
|
||||
this.subprotocols = [ ...this.subprotocols, subprotocol ];
|
||||
}
|
||||
}
|
||||
|
||||
function createWebSocketURL(url) {
|
||||
if (typeof url === "function") {
|
||||
url = url();
|
||||
}
|
||||
if (url && !/^wss?:/i.test(url)) {
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.href = a.href;
|
||||
a.protocol = a.protocol.replace("http", "ws");
|
||||
return a.href;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) {
|
||||
return new Consumer(url);
|
||||
}
|
||||
|
||||
function getConfig(name) {
|
||||
const element = document.head.querySelector(`meta[name='action-cable-${name}']`);
|
||||
if (element) {
|
||||
return element.getAttribute("content");
|
||||
}
|
||||
}
|
||||
|
||||
export { Connection, ConnectionMonitor, Consumer, INTERNAL, Subscription, SubscriptionGuarantor, Subscriptions, adapters, createConsumer, createWebSocketURL, getConfig, logger };
|
||||
Binary file not shown.
@@ -1,884 +0,0 @@
|
||||
(function(factory) {
|
||||
typeof define === "function" && define.amd ? define(factory) : factory();
|
||||
})((function() {
|
||||
"use strict";
|
||||
var sparkMd5 = {
|
||||
exports: {}
|
||||
};
|
||||
(function(module, exports) {
|
||||
(function(factory) {
|
||||
{
|
||||
module.exports = factory();
|
||||
}
|
||||
})((function(undefined$1) {
|
||||
var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ];
|
||||
function md5cycle(x, k) {
|
||||
var a = x[0], b = x[1], c = x[2], d = x[3];
|
||||
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[10] - 42063 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b ^ c ^ d) + k[5] - 378558 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
x[0] = a + x[0] | 0;
|
||||
x[1] = b + x[1] | 0;
|
||||
x[2] = c + x[2] | 0;
|
||||
x[3] = d + x[3] | 0;
|
||||
}
|
||||
function md5blk(s) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md5blk_array(a) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md51(s) {
|
||||
var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
||||
}
|
||||
s = s.substring(i - 64);
|
||||
length = s.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function md51_array(a) {
|
||||
var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
|
||||
}
|
||||
a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0);
|
||||
length = a.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= a[i] << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function rhex(n) {
|
||||
var s = "", j;
|
||||
for (j = 0; j < 4; j += 1) {
|
||||
s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function hex(x) {
|
||||
var i;
|
||||
for (i = 0; i < x.length; i += 1) {
|
||||
x[i] = rhex(x[i]);
|
||||
}
|
||||
return x.join("");
|
||||
}
|
||||
if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ;
|
||||
if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) {
|
||||
(function() {
|
||||
function clamp(val, length) {
|
||||
val = val | 0 || 0;
|
||||
if (val < 0) {
|
||||
return Math.max(val + length, 0);
|
||||
}
|
||||
return Math.min(val, length);
|
||||
}
|
||||
ArrayBuffer.prototype.slice = function(from, to) {
|
||||
var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray;
|
||||
if (to !== undefined$1) {
|
||||
end = clamp(to, length);
|
||||
}
|
||||
if (begin > end) {
|
||||
return new ArrayBuffer(0);
|
||||
}
|
||||
num = end - begin;
|
||||
target = new ArrayBuffer(num);
|
||||
targetArray = new Uint8Array(target);
|
||||
sourceArray = new Uint8Array(this, begin, num);
|
||||
targetArray.set(sourceArray);
|
||||
return target;
|
||||
};
|
||||
})();
|
||||
}
|
||||
function toUtf8(str) {
|
||||
if (/[\u0080-\uFFFF]/.test(str)) {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
||||
var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
arr[i] = str.charCodeAt(i);
|
||||
}
|
||||
return returnUInt8Array ? arr : buff;
|
||||
}
|
||||
function arrayBuffer2Utf8Str(buff) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buff));
|
||||
}
|
||||
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
||||
var result = new Uint8Array(first.byteLength + second.byteLength);
|
||||
result.set(new Uint8Array(first));
|
||||
result.set(new Uint8Array(second), first.byteLength);
|
||||
return returnUInt8Array ? result : result.buffer;
|
||||
}
|
||||
function hexToBinaryString(hex) {
|
||||
var bytes = [], length = hex.length, x;
|
||||
for (x = 0; x < length - 1; x += 2) {
|
||||
bytes.push(parseInt(hex.substr(x, 2), 16));
|
||||
}
|
||||
return String.fromCharCode.apply(String, bytes);
|
||||
}
|
||||
function SparkMD5() {
|
||||
this.reset();
|
||||
}
|
||||
SparkMD5.prototype.append = function(str) {
|
||||
this.appendBinary(toUtf8(str));
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.appendBinary = function(contents) {
|
||||
this._buff += contents;
|
||||
this._length += contents.length;
|
||||
var length = this._buff.length, i;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
|
||||
}
|
||||
this._buff = this._buff.substring(i - 64);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.prototype.reset = function() {
|
||||
this._buff = "";
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.getState = function() {
|
||||
return {
|
||||
buff: this._buff,
|
||||
length: this._length,
|
||||
hash: this._hash.slice()
|
||||
};
|
||||
};
|
||||
SparkMD5.prototype.setState = function(state) {
|
||||
this._buff = state.buff;
|
||||
this._length = state.length;
|
||||
this._hash = state.hash;
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.destroy = function() {
|
||||
delete this._hash;
|
||||
delete this._buff;
|
||||
delete this._length;
|
||||
};
|
||||
SparkMD5.prototype._finish = function(tail, length) {
|
||||
var i = length, tmp, lo, hi;
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(this._hash, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = this._length * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(this._hash, tail);
|
||||
};
|
||||
SparkMD5.hash = function(str, raw) {
|
||||
return SparkMD5.hashBinary(toUtf8(str), raw);
|
||||
};
|
||||
SparkMD5.hashBinary = function(content, raw) {
|
||||
var hash = md51(content), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer = function() {
|
||||
this.reset();
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
|
||||
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i;
|
||||
this._length += arr.byteLength;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
|
||||
}
|
||||
this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff[i] << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.reset = function() {
|
||||
this._buff = new Uint8Array(0);
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.getState = function() {
|
||||
var state = SparkMD5.prototype.getState.call(this);
|
||||
state.buff = arrayBuffer2Utf8Str(state.buff);
|
||||
return state;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
|
||||
state.buff = utf8Str2ArrayBuffer(state.buff, true);
|
||||
return SparkMD5.prototype.setState.call(this, state);
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
|
||||
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
|
||||
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
|
||||
var hash = md51_array(new Uint8Array(arr)), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
return SparkMD5;
|
||||
}));
|
||||
})(sparkMd5);
|
||||
var SparkMD5 = sparkMd5.exports;
|
||||
const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
|
||||
class FileChecksum {
|
||||
static create(file, callback) {
|
||||
const instance = new FileChecksum(file);
|
||||
instance.create(callback);
|
||||
}
|
||||
constructor(file) {
|
||||
this.file = file;
|
||||
this.chunkSize = 2097152;
|
||||
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
|
||||
this.chunkIndex = 0;
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.md5Buffer = new SparkMD5.ArrayBuffer;
|
||||
this.fileReader = new FileReader;
|
||||
this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event)));
|
||||
this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event)));
|
||||
this.readNextChunk();
|
||||
}
|
||||
fileReaderDidLoad(event) {
|
||||
this.md5Buffer.append(event.target.result);
|
||||
if (!this.readNextChunk()) {
|
||||
const binaryDigest = this.md5Buffer.end(true);
|
||||
const base64digest = btoa(binaryDigest);
|
||||
this.callback(null, base64digest);
|
||||
}
|
||||
}
|
||||
fileReaderDidError(event) {
|
||||
this.callback(`Error reading ${this.file.name}`);
|
||||
}
|
||||
readNextChunk() {
|
||||
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
|
||||
const start = this.chunkIndex * this.chunkSize;
|
||||
const end = Math.min(start + this.chunkSize, this.file.size);
|
||||
const bytes = fileSlice.call(this.file, start, end);
|
||||
this.fileReader.readAsArrayBuffer(bytes);
|
||||
this.chunkIndex++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
function getMetaValue(name) {
|
||||
const element = findElement(document.head, `meta[name="${name}"]`);
|
||||
if (element) {
|
||||
return element.getAttribute("content");
|
||||
}
|
||||
}
|
||||
function findElements(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
const elements = root.querySelectorAll(selector);
|
||||
return toArray(elements);
|
||||
}
|
||||
function findElement(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
return root.querySelector(selector);
|
||||
}
|
||||
function dispatchEvent(element, type, eventInit = {}) {
|
||||
const {disabled: disabled} = element;
|
||||
const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit;
|
||||
const event = document.createEvent("Event");
|
||||
event.initEvent(type, bubbles || true, cancelable || true);
|
||||
event.detail = detail || {};
|
||||
try {
|
||||
element.disabled = false;
|
||||
element.dispatchEvent(event);
|
||||
} finally {
|
||||
element.disabled = disabled;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
function toArray(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
} else if (Array.from) {
|
||||
return Array.from(value);
|
||||
} else {
|
||||
return [].slice.call(value);
|
||||
}
|
||||
}
|
||||
class BlobRecord {
|
||||
constructor(file, checksum, url, customHeaders = {}) {
|
||||
this.file = file;
|
||||
this.attributes = {
|
||||
filename: file.name,
|
||||
content_type: file.type || "application/octet-stream",
|
||||
byte_size: file.size,
|
||||
checksum: checksum
|
||||
};
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("POST", url, true);
|
||||
this.xhr.responseType = "json";
|
||||
this.xhr.setRequestHeader("Content-Type", "application/json");
|
||||
this.xhr.setRequestHeader("Accept", "application/json");
|
||||
this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
Object.keys(customHeaders).forEach((headerKey => {
|
||||
this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]);
|
||||
}));
|
||||
const csrfToken = getMetaValue("csrf-token");
|
||||
if (csrfToken != undefined) {
|
||||
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
get status() {
|
||||
return this.xhr.status;
|
||||
}
|
||||
get response() {
|
||||
const {responseType: responseType, response: response} = this.xhr;
|
||||
if (responseType == "json") {
|
||||
return response;
|
||||
} else {
|
||||
return JSON.parse(response);
|
||||
}
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(JSON.stringify({
|
||||
blob: this.attributes
|
||||
}));
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
if (this.status >= 200 && this.status < 300) {
|
||||
const {response: response} = this;
|
||||
const {direct_upload: direct_upload} = response;
|
||||
delete response.direct_upload;
|
||||
this.attributes = response;
|
||||
this.directUploadData = direct_upload;
|
||||
this.callback(null, this.toJSON());
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`);
|
||||
}
|
||||
toJSON() {
|
||||
const result = {};
|
||||
for (const key in this.attributes) {
|
||||
result[key] = this.attributes[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
class BlobUpload {
|
||||
constructor(blob) {
|
||||
this.blob = blob;
|
||||
this.file = blob.file;
|
||||
const {url: url, headers: headers} = blob.directUploadData;
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("PUT", url, true);
|
||||
this.xhr.responseType = "text";
|
||||
for (const key in headers) {
|
||||
this.xhr.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(this.file.slice());
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
const {status: status, response: response} = this.xhr;
|
||||
if (status >= 200 && status < 300) {
|
||||
this.callback(null, response);
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
|
||||
}
|
||||
}
|
||||
let id = 0;
|
||||
class DirectUpload {
|
||||
constructor(file, url, delegate, customHeaders = {}) {
|
||||
this.id = ++id;
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
this.delegate = delegate;
|
||||
this.customHeaders = customHeaders;
|
||||
}
|
||||
create(callback) {
|
||||
FileChecksum.create(this.file, ((error, checksum) => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders);
|
||||
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
||||
blob.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
const upload = new BlobUpload(blob);
|
||||
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
|
||||
upload.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
callback(null, blob.toJSON());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
function notify(object, methodName, ...messages) {
|
||||
if (object && typeof object[methodName] == "function") {
|
||||
return object[methodName](...messages);
|
||||
}
|
||||
}
|
||||
class DirectUploadController {
|
||||
constructor(input, file) {
|
||||
this.input = input;
|
||||
this.file = file;
|
||||
this.directUpload = new DirectUpload(this.file, this.url, this);
|
||||
this.dispatch("initialize");
|
||||
}
|
||||
start(callback) {
|
||||
const hiddenInput = document.createElement("input");
|
||||
hiddenInput.type = "hidden";
|
||||
hiddenInput.name = this.input.name;
|
||||
this.input.insertAdjacentElement("beforebegin", hiddenInput);
|
||||
this.dispatch("start");
|
||||
this.directUpload.create(((error, attributes) => {
|
||||
if (error) {
|
||||
hiddenInput.parentNode.removeChild(hiddenInput);
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
hiddenInput.value = attributes.signed_id;
|
||||
}
|
||||
this.dispatch("end");
|
||||
callback(error);
|
||||
}));
|
||||
}
|
||||
uploadRequestDidProgress(event) {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}
|
||||
get url() {
|
||||
return this.input.getAttribute("data-direct-upload-url");
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.file = this.file;
|
||||
detail.id = this.directUpload.id;
|
||||
return dispatchEvent(this.input, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
directUploadWillCreateBlobWithXHR(xhr) {
|
||||
this.dispatch("before-blob-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
this.dispatch("before-storage-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
||||
}
|
||||
}
|
||||
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
||||
class DirectUploadsController {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
this.inputs = findElements(form, inputSelector).filter((input => input.files.length));
|
||||
}
|
||||
start(callback) {
|
||||
const controllers = this.createDirectUploadControllers();
|
||||
const startNextController = () => {
|
||||
const controller = controllers.shift();
|
||||
if (controller) {
|
||||
controller.start((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
this.dispatch("end");
|
||||
} else {
|
||||
startNextController();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
callback();
|
||||
this.dispatch("end");
|
||||
}
|
||||
};
|
||||
this.dispatch("start");
|
||||
startNextController();
|
||||
}
|
||||
createDirectUploadControllers() {
|
||||
const controllers = [];
|
||||
this.inputs.forEach((input => {
|
||||
toArray(input.files).forEach((file => {
|
||||
const controller = new DirectUploadController(input, file);
|
||||
controllers.push(controller);
|
||||
}));
|
||||
}));
|
||||
return controllers;
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
return dispatchEvent(this.form, `direct-uploads:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
}
|
||||
const processingAttribute = "data-direct-uploads-processing";
|
||||
const submitButtonsByForm = new WeakMap;
|
||||
let started = false;
|
||||
function start() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
document.addEventListener("click", didClick, true);
|
||||
document.addEventListener("submit", didSubmitForm, true);
|
||||
document.addEventListener("ajax:before", didSubmitRemoteElement);
|
||||
}
|
||||
}
|
||||
function didClick(event) {
|
||||
const button = event.target.closest("button, input");
|
||||
if (button && button.type === "submit" && button.form) {
|
||||
submitButtonsByForm.set(button.form, button);
|
||||
}
|
||||
}
|
||||
function didSubmitForm(event) {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
function didSubmitRemoteElement(event) {
|
||||
if (event.target.tagName == "FORM") {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
}
|
||||
function handleFormSubmissionEvent(event) {
|
||||
const form = event.target;
|
||||
if (form.hasAttribute(processingAttribute)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
const controller = new DirectUploadsController(form);
|
||||
const {inputs: inputs} = controller;
|
||||
if (inputs.length) {
|
||||
event.preventDefault();
|
||||
form.setAttribute(processingAttribute, "");
|
||||
inputs.forEach(disable);
|
||||
controller.start((error => {
|
||||
form.removeAttribute(processingAttribute);
|
||||
if (error) {
|
||||
inputs.forEach(enable);
|
||||
} else {
|
||||
submitForm(form);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
function submitForm(form) {
|
||||
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
|
||||
if (button) {
|
||||
const {disabled: disabled} = button;
|
||||
button.disabled = false;
|
||||
button.focus();
|
||||
button.click();
|
||||
button.disabled = disabled;
|
||||
} else {
|
||||
button = document.createElement("input");
|
||||
button.type = "submit";
|
||||
button.style.display = "none";
|
||||
form.appendChild(button);
|
||||
button.click();
|
||||
form.removeChild(button);
|
||||
}
|
||||
submitButtonsByForm.delete(form);
|
||||
}
|
||||
function disable(input) {
|
||||
input.disabled = true;
|
||||
}
|
||||
function enable(input) {
|
||||
input.disabled = false;
|
||||
}
|
||||
function autostart() {
|
||||
if (window.ActiveStorage) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
setTimeout(autostart, 1);
|
||||
class AttachmentUpload {
|
||||
constructor(attachment, element) {
|
||||
this.attachment = attachment;
|
||||
this.element = element;
|
||||
this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this);
|
||||
}
|
||||
start() {
|
||||
this.directUpload.create(this.directUploadDidComplete.bind(this));
|
||||
this.dispatch("start");
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
xhr.upload.addEventListener("progress", (event => {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
this.attachment.setUploadProgress(progress);
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
directUploadDidComplete(error, attributes) {
|
||||
if (error) {
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
});
|
||||
this.dispatch("end");
|
||||
}
|
||||
}
|
||||
createBlobUrl(signedId, filename) {
|
||||
return this.blobUrlTemplate.replace(":signed_id", signedId).replace(":filename", encodeURIComponent(filename));
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.attachment = this.attachment;
|
||||
return dispatchEvent(this.element, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
get directUploadUrl() {
|
||||
return this.element.dataset.directUploadUrl;
|
||||
}
|
||||
get blobUrlTemplate() {
|
||||
return this.element.dataset.blobUrlTemplate;
|
||||
}
|
||||
}
|
||||
addEventListener("trix-attachment-add", (event => {
|
||||
const {attachment: attachment, target: target} = event;
|
||||
if (attachment.file) {
|
||||
const upload = new AttachmentUpload(attachment, target);
|
||||
upload.start();
|
||||
}
|
||||
}));
|
||||
}));
|
||||
Binary file not shown.
@@ -1,911 +0,0 @@
|
||||
var sparkMd5 = {
|
||||
exports: {}
|
||||
};
|
||||
|
||||
(function(module, exports) {
|
||||
(function(factory) {
|
||||
{
|
||||
module.exports = factory();
|
||||
}
|
||||
})((function(undefined$1) {
|
||||
var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ];
|
||||
function md5cycle(x, k) {
|
||||
var a = x[0], b = x[1], c = x[2], d = x[3];
|
||||
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[10] - 42063 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b ^ c ^ d) + k[5] - 378558 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
x[0] = a + x[0] | 0;
|
||||
x[1] = b + x[1] | 0;
|
||||
x[2] = c + x[2] | 0;
|
||||
x[3] = d + x[3] | 0;
|
||||
}
|
||||
function md5blk(s) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md5blk_array(a) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md51(s) {
|
||||
var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
||||
}
|
||||
s = s.substring(i - 64);
|
||||
length = s.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function md51_array(a) {
|
||||
var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
|
||||
}
|
||||
a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0);
|
||||
length = a.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= a[i] << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function rhex(n) {
|
||||
var s = "", j;
|
||||
for (j = 0; j < 4; j += 1) {
|
||||
s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function hex(x) {
|
||||
var i;
|
||||
for (i = 0; i < x.length; i += 1) {
|
||||
x[i] = rhex(x[i]);
|
||||
}
|
||||
return x.join("");
|
||||
}
|
||||
if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ;
|
||||
if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) {
|
||||
(function() {
|
||||
function clamp(val, length) {
|
||||
val = val | 0 || 0;
|
||||
if (val < 0) {
|
||||
return Math.max(val + length, 0);
|
||||
}
|
||||
return Math.min(val, length);
|
||||
}
|
||||
ArrayBuffer.prototype.slice = function(from, to) {
|
||||
var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray;
|
||||
if (to !== undefined$1) {
|
||||
end = clamp(to, length);
|
||||
}
|
||||
if (begin > end) {
|
||||
return new ArrayBuffer(0);
|
||||
}
|
||||
num = end - begin;
|
||||
target = new ArrayBuffer(num);
|
||||
targetArray = new Uint8Array(target);
|
||||
sourceArray = new Uint8Array(this, begin, num);
|
||||
targetArray.set(sourceArray);
|
||||
return target;
|
||||
};
|
||||
})();
|
||||
}
|
||||
function toUtf8(str) {
|
||||
if (/[\u0080-\uFFFF]/.test(str)) {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
||||
var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
arr[i] = str.charCodeAt(i);
|
||||
}
|
||||
return returnUInt8Array ? arr : buff;
|
||||
}
|
||||
function arrayBuffer2Utf8Str(buff) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buff));
|
||||
}
|
||||
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
||||
var result = new Uint8Array(first.byteLength + second.byteLength);
|
||||
result.set(new Uint8Array(first));
|
||||
result.set(new Uint8Array(second), first.byteLength);
|
||||
return returnUInt8Array ? result : result.buffer;
|
||||
}
|
||||
function hexToBinaryString(hex) {
|
||||
var bytes = [], length = hex.length, x;
|
||||
for (x = 0; x < length - 1; x += 2) {
|
||||
bytes.push(parseInt(hex.substr(x, 2), 16));
|
||||
}
|
||||
return String.fromCharCode.apply(String, bytes);
|
||||
}
|
||||
function SparkMD5() {
|
||||
this.reset();
|
||||
}
|
||||
SparkMD5.prototype.append = function(str) {
|
||||
this.appendBinary(toUtf8(str));
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.appendBinary = function(contents) {
|
||||
this._buff += contents;
|
||||
this._length += contents.length;
|
||||
var length = this._buff.length, i;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
|
||||
}
|
||||
this._buff = this._buff.substring(i - 64);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.prototype.reset = function() {
|
||||
this._buff = "";
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.getState = function() {
|
||||
return {
|
||||
buff: this._buff,
|
||||
length: this._length,
|
||||
hash: this._hash.slice()
|
||||
};
|
||||
};
|
||||
SparkMD5.prototype.setState = function(state) {
|
||||
this._buff = state.buff;
|
||||
this._length = state.length;
|
||||
this._hash = state.hash;
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.destroy = function() {
|
||||
delete this._hash;
|
||||
delete this._buff;
|
||||
delete this._length;
|
||||
};
|
||||
SparkMD5.prototype._finish = function(tail, length) {
|
||||
var i = length, tmp, lo, hi;
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(this._hash, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = this._length * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(this._hash, tail);
|
||||
};
|
||||
SparkMD5.hash = function(str, raw) {
|
||||
return SparkMD5.hashBinary(toUtf8(str), raw);
|
||||
};
|
||||
SparkMD5.hashBinary = function(content, raw) {
|
||||
var hash = md51(content), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer = function() {
|
||||
this.reset();
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
|
||||
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i;
|
||||
this._length += arr.byteLength;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
|
||||
}
|
||||
this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff[i] << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.reset = function() {
|
||||
this._buff = new Uint8Array(0);
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.getState = function() {
|
||||
var state = SparkMD5.prototype.getState.call(this);
|
||||
state.buff = arrayBuffer2Utf8Str(state.buff);
|
||||
return state;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
|
||||
state.buff = utf8Str2ArrayBuffer(state.buff, true);
|
||||
return SparkMD5.prototype.setState.call(this, state);
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
|
||||
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
|
||||
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
|
||||
var hash = md51_array(new Uint8Array(arr)), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
return SparkMD5;
|
||||
}));
|
||||
})(sparkMd5);
|
||||
|
||||
var SparkMD5 = sparkMd5.exports;
|
||||
|
||||
const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
|
||||
|
||||
class FileChecksum {
|
||||
static create(file, callback) {
|
||||
const instance = new FileChecksum(file);
|
||||
instance.create(callback);
|
||||
}
|
||||
constructor(file) {
|
||||
this.file = file;
|
||||
this.chunkSize = 2097152;
|
||||
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
|
||||
this.chunkIndex = 0;
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.md5Buffer = new SparkMD5.ArrayBuffer;
|
||||
this.fileReader = new FileReader;
|
||||
this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event)));
|
||||
this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event)));
|
||||
this.readNextChunk();
|
||||
}
|
||||
fileReaderDidLoad(event) {
|
||||
this.md5Buffer.append(event.target.result);
|
||||
if (!this.readNextChunk()) {
|
||||
const binaryDigest = this.md5Buffer.end(true);
|
||||
const base64digest = btoa(binaryDigest);
|
||||
this.callback(null, base64digest);
|
||||
}
|
||||
}
|
||||
fileReaderDidError(event) {
|
||||
this.callback(`Error reading ${this.file.name}`);
|
||||
}
|
||||
readNextChunk() {
|
||||
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
|
||||
const start = this.chunkIndex * this.chunkSize;
|
||||
const end = Math.min(start + this.chunkSize, this.file.size);
|
||||
const bytes = fileSlice.call(this.file, start, end);
|
||||
this.fileReader.readAsArrayBuffer(bytes);
|
||||
this.chunkIndex++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMetaValue(name) {
|
||||
const element = findElement(document.head, `meta[name="${name}"]`);
|
||||
if (element) {
|
||||
return element.getAttribute("content");
|
||||
}
|
||||
}
|
||||
|
||||
function findElements(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
const elements = root.querySelectorAll(selector);
|
||||
return toArray(elements);
|
||||
}
|
||||
|
||||
function findElement(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
return root.querySelector(selector);
|
||||
}
|
||||
|
||||
function dispatchEvent(element, type, eventInit = {}) {
|
||||
const {disabled: disabled} = element;
|
||||
const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit;
|
||||
const event = document.createEvent("Event");
|
||||
event.initEvent(type, bubbles || true, cancelable || true);
|
||||
event.detail = detail || {};
|
||||
try {
|
||||
element.disabled = false;
|
||||
element.dispatchEvent(event);
|
||||
} finally {
|
||||
element.disabled = disabled;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
function toArray(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
} else if (Array.from) {
|
||||
return Array.from(value);
|
||||
} else {
|
||||
return [].slice.call(value);
|
||||
}
|
||||
}
|
||||
|
||||
class BlobRecord {
|
||||
constructor(file, checksum, url, customHeaders = {}) {
|
||||
this.file = file;
|
||||
this.attributes = {
|
||||
filename: file.name,
|
||||
content_type: file.type || "application/octet-stream",
|
||||
byte_size: file.size,
|
||||
checksum: checksum
|
||||
};
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("POST", url, true);
|
||||
this.xhr.responseType = "json";
|
||||
this.xhr.setRequestHeader("Content-Type", "application/json");
|
||||
this.xhr.setRequestHeader("Accept", "application/json");
|
||||
this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
Object.keys(customHeaders).forEach((headerKey => {
|
||||
this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]);
|
||||
}));
|
||||
const csrfToken = getMetaValue("csrf-token");
|
||||
if (csrfToken != undefined) {
|
||||
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
get status() {
|
||||
return this.xhr.status;
|
||||
}
|
||||
get response() {
|
||||
const {responseType: responseType, response: response} = this.xhr;
|
||||
if (responseType == "json") {
|
||||
return response;
|
||||
} else {
|
||||
return JSON.parse(response);
|
||||
}
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(JSON.stringify({
|
||||
blob: this.attributes
|
||||
}));
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
if (this.status >= 200 && this.status < 300) {
|
||||
const {response: response} = this;
|
||||
const {direct_upload: direct_upload} = response;
|
||||
delete response.direct_upload;
|
||||
this.attributes = response;
|
||||
this.directUploadData = direct_upload;
|
||||
this.callback(null, this.toJSON());
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`);
|
||||
}
|
||||
toJSON() {
|
||||
const result = {};
|
||||
for (const key in this.attributes) {
|
||||
result[key] = this.attributes[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class BlobUpload {
|
||||
constructor(blob) {
|
||||
this.blob = blob;
|
||||
this.file = blob.file;
|
||||
const {url: url, headers: headers} = blob.directUploadData;
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("PUT", url, true);
|
||||
this.xhr.responseType = "text";
|
||||
for (const key in headers) {
|
||||
this.xhr.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(this.file.slice());
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
const {status: status, response: response} = this.xhr;
|
||||
if (status >= 200 && status < 300) {
|
||||
this.callback(null, response);
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
let id = 0;
|
||||
|
||||
class DirectUpload {
|
||||
constructor(file, url, delegate, customHeaders = {}) {
|
||||
this.id = ++id;
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
this.delegate = delegate;
|
||||
this.customHeaders = customHeaders;
|
||||
}
|
||||
create(callback) {
|
||||
FileChecksum.create(this.file, ((error, checksum) => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders);
|
||||
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
||||
blob.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
const upload = new BlobUpload(blob);
|
||||
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
|
||||
upload.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
callback(null, blob.toJSON());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function notify(object, methodName, ...messages) {
|
||||
if (object && typeof object[methodName] == "function") {
|
||||
return object[methodName](...messages);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectUploadController {
|
||||
constructor(input, file) {
|
||||
this.input = input;
|
||||
this.file = file;
|
||||
this.directUpload = new DirectUpload(this.file, this.url, this);
|
||||
this.dispatch("initialize");
|
||||
}
|
||||
start(callback) {
|
||||
const hiddenInput = document.createElement("input");
|
||||
hiddenInput.type = "hidden";
|
||||
hiddenInput.name = this.input.name;
|
||||
this.input.insertAdjacentElement("beforebegin", hiddenInput);
|
||||
this.dispatch("start");
|
||||
this.directUpload.create(((error, attributes) => {
|
||||
if (error) {
|
||||
hiddenInput.parentNode.removeChild(hiddenInput);
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
hiddenInput.value = attributes.signed_id;
|
||||
}
|
||||
this.dispatch("end");
|
||||
callback(error);
|
||||
}));
|
||||
}
|
||||
uploadRequestDidProgress(event) {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}
|
||||
get url() {
|
||||
return this.input.getAttribute("data-direct-upload-url");
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.file = this.file;
|
||||
detail.id = this.directUpload.id;
|
||||
return dispatchEvent(this.input, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
directUploadWillCreateBlobWithXHR(xhr) {
|
||||
this.dispatch("before-blob-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
this.dispatch("before-storage-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
||||
}
|
||||
}
|
||||
|
||||
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
||||
|
||||
class DirectUploadsController {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
this.inputs = findElements(form, inputSelector).filter((input => input.files.length));
|
||||
}
|
||||
start(callback) {
|
||||
const controllers = this.createDirectUploadControllers();
|
||||
const startNextController = () => {
|
||||
const controller = controllers.shift();
|
||||
if (controller) {
|
||||
controller.start((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
this.dispatch("end");
|
||||
} else {
|
||||
startNextController();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
callback();
|
||||
this.dispatch("end");
|
||||
}
|
||||
};
|
||||
this.dispatch("start");
|
||||
startNextController();
|
||||
}
|
||||
createDirectUploadControllers() {
|
||||
const controllers = [];
|
||||
this.inputs.forEach((input => {
|
||||
toArray(input.files).forEach((file => {
|
||||
const controller = new DirectUploadController(input, file);
|
||||
controllers.push(controller);
|
||||
}));
|
||||
}));
|
||||
return controllers;
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
return dispatchEvent(this.form, `direct-uploads:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const processingAttribute = "data-direct-uploads-processing";
|
||||
|
||||
const submitButtonsByForm = new WeakMap;
|
||||
|
||||
let started = false;
|
||||
|
||||
function start() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
document.addEventListener("click", didClick, true);
|
||||
document.addEventListener("submit", didSubmitForm, true);
|
||||
document.addEventListener("ajax:before", didSubmitRemoteElement);
|
||||
}
|
||||
}
|
||||
|
||||
function didClick(event) {
|
||||
const button = event.target.closest("button, input");
|
||||
if (button && button.type === "submit" && button.form) {
|
||||
submitButtonsByForm.set(button.form, button);
|
||||
}
|
||||
}
|
||||
|
||||
function didSubmitForm(event) {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
|
||||
function didSubmitRemoteElement(event) {
|
||||
if (event.target.tagName == "FORM") {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function handleFormSubmissionEvent(event) {
|
||||
const form = event.target;
|
||||
if (form.hasAttribute(processingAttribute)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
const controller = new DirectUploadsController(form);
|
||||
const {inputs: inputs} = controller;
|
||||
if (inputs.length) {
|
||||
event.preventDefault();
|
||||
form.setAttribute(processingAttribute, "");
|
||||
inputs.forEach(disable);
|
||||
controller.start((error => {
|
||||
form.removeAttribute(processingAttribute);
|
||||
if (error) {
|
||||
inputs.forEach(enable);
|
||||
} else {
|
||||
submitForm(form);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function submitForm(form) {
|
||||
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
|
||||
if (button) {
|
||||
const {disabled: disabled} = button;
|
||||
button.disabled = false;
|
||||
button.focus();
|
||||
button.click();
|
||||
button.disabled = disabled;
|
||||
} else {
|
||||
button = document.createElement("input");
|
||||
button.type = "submit";
|
||||
button.style.display = "none";
|
||||
form.appendChild(button);
|
||||
button.click();
|
||||
form.removeChild(button);
|
||||
}
|
||||
submitButtonsByForm.delete(form);
|
||||
}
|
||||
|
||||
function disable(input) {
|
||||
input.disabled = true;
|
||||
}
|
||||
|
||||
function enable(input) {
|
||||
input.disabled = false;
|
||||
}
|
||||
|
||||
function autostart() {
|
||||
if (window.ActiveStorage) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(autostart, 1);
|
||||
|
||||
class AttachmentUpload {
|
||||
constructor(attachment, element) {
|
||||
this.attachment = attachment;
|
||||
this.element = element;
|
||||
this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this);
|
||||
}
|
||||
start() {
|
||||
this.directUpload.create(this.directUploadDidComplete.bind(this));
|
||||
this.dispatch("start");
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
xhr.upload.addEventListener("progress", (event => {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
this.attachment.setUploadProgress(progress);
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
directUploadDidComplete(error, attributes) {
|
||||
if (error) {
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
});
|
||||
this.dispatch("end");
|
||||
}
|
||||
}
|
||||
createBlobUrl(signedId, filename) {
|
||||
return this.blobUrlTemplate.replace(":signed_id", signedId).replace(":filename", encodeURIComponent(filename));
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.attachment = this.attachment;
|
||||
return dispatchEvent(this.element, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
get directUploadUrl() {
|
||||
return this.element.dataset.directUploadUrl;
|
||||
}
|
||||
get blobUrlTemplate() {
|
||||
return this.element.dataset.blobUrlTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener("trix-attachment-add", (event => {
|
||||
const {attachment: attachment, target: target} = event;
|
||||
if (attachment.file) {
|
||||
const upload = new AttachmentUpload(attachment, target);
|
||||
upload.start();
|
||||
}
|
||||
}));
|
||||
Binary file not shown.
@@ -1,830 +0,0 @@
|
||||
(function(global, factory) {
|
||||
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
|
||||
factory(global.ActiveStorage = {}));
|
||||
})(this, (function(exports) {
|
||||
"use strict";
|
||||
var sparkMd5 = {
|
||||
exports: {}
|
||||
};
|
||||
(function(module, exports) {
|
||||
(function(factory) {
|
||||
{
|
||||
module.exports = factory();
|
||||
}
|
||||
})((function(undefined$1) {
|
||||
var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ];
|
||||
function md5cycle(x, k) {
|
||||
var a = x[0], b = x[1], c = x[2], d = x[3];
|
||||
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[10] - 42063 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b ^ c ^ d) + k[5] - 378558 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
x[0] = a + x[0] | 0;
|
||||
x[1] = b + x[1] | 0;
|
||||
x[2] = c + x[2] | 0;
|
||||
x[3] = d + x[3] | 0;
|
||||
}
|
||||
function md5blk(s) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md5blk_array(a) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md51(s) {
|
||||
var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
||||
}
|
||||
s = s.substring(i - 64);
|
||||
length = s.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function md51_array(a) {
|
||||
var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
|
||||
}
|
||||
a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0);
|
||||
length = a.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= a[i] << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function rhex(n) {
|
||||
var s = "", j;
|
||||
for (j = 0; j < 4; j += 1) {
|
||||
s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function hex(x) {
|
||||
var i;
|
||||
for (i = 0; i < x.length; i += 1) {
|
||||
x[i] = rhex(x[i]);
|
||||
}
|
||||
return x.join("");
|
||||
}
|
||||
if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ;
|
||||
if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) {
|
||||
(function() {
|
||||
function clamp(val, length) {
|
||||
val = val | 0 || 0;
|
||||
if (val < 0) {
|
||||
return Math.max(val + length, 0);
|
||||
}
|
||||
return Math.min(val, length);
|
||||
}
|
||||
ArrayBuffer.prototype.slice = function(from, to) {
|
||||
var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray;
|
||||
if (to !== undefined$1) {
|
||||
end = clamp(to, length);
|
||||
}
|
||||
if (begin > end) {
|
||||
return new ArrayBuffer(0);
|
||||
}
|
||||
num = end - begin;
|
||||
target = new ArrayBuffer(num);
|
||||
targetArray = new Uint8Array(target);
|
||||
sourceArray = new Uint8Array(this, begin, num);
|
||||
targetArray.set(sourceArray);
|
||||
return target;
|
||||
};
|
||||
})();
|
||||
}
|
||||
function toUtf8(str) {
|
||||
if (/[\u0080-\uFFFF]/.test(str)) {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
||||
var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
arr[i] = str.charCodeAt(i);
|
||||
}
|
||||
return returnUInt8Array ? arr : buff;
|
||||
}
|
||||
function arrayBuffer2Utf8Str(buff) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buff));
|
||||
}
|
||||
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
||||
var result = new Uint8Array(first.byteLength + second.byteLength);
|
||||
result.set(new Uint8Array(first));
|
||||
result.set(new Uint8Array(second), first.byteLength);
|
||||
return returnUInt8Array ? result : result.buffer;
|
||||
}
|
||||
function hexToBinaryString(hex) {
|
||||
var bytes = [], length = hex.length, x;
|
||||
for (x = 0; x < length - 1; x += 2) {
|
||||
bytes.push(parseInt(hex.substr(x, 2), 16));
|
||||
}
|
||||
return String.fromCharCode.apply(String, bytes);
|
||||
}
|
||||
function SparkMD5() {
|
||||
this.reset();
|
||||
}
|
||||
SparkMD5.prototype.append = function(str) {
|
||||
this.appendBinary(toUtf8(str));
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.appendBinary = function(contents) {
|
||||
this._buff += contents;
|
||||
this._length += contents.length;
|
||||
var length = this._buff.length, i;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
|
||||
}
|
||||
this._buff = this._buff.substring(i - 64);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.prototype.reset = function() {
|
||||
this._buff = "";
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.getState = function() {
|
||||
return {
|
||||
buff: this._buff,
|
||||
length: this._length,
|
||||
hash: this._hash.slice()
|
||||
};
|
||||
};
|
||||
SparkMD5.prototype.setState = function(state) {
|
||||
this._buff = state.buff;
|
||||
this._length = state.length;
|
||||
this._hash = state.hash;
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.destroy = function() {
|
||||
delete this._hash;
|
||||
delete this._buff;
|
||||
delete this._length;
|
||||
};
|
||||
SparkMD5.prototype._finish = function(tail, length) {
|
||||
var i = length, tmp, lo, hi;
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(this._hash, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = this._length * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(this._hash, tail);
|
||||
};
|
||||
SparkMD5.hash = function(str, raw) {
|
||||
return SparkMD5.hashBinary(toUtf8(str), raw);
|
||||
};
|
||||
SparkMD5.hashBinary = function(content, raw) {
|
||||
var hash = md51(content), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer = function() {
|
||||
this.reset();
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
|
||||
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i;
|
||||
this._length += arr.byteLength;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
|
||||
}
|
||||
this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff[i] << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.reset = function() {
|
||||
this._buff = new Uint8Array(0);
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.getState = function() {
|
||||
var state = SparkMD5.prototype.getState.call(this);
|
||||
state.buff = arrayBuffer2Utf8Str(state.buff);
|
||||
return state;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
|
||||
state.buff = utf8Str2ArrayBuffer(state.buff, true);
|
||||
return SparkMD5.prototype.setState.call(this, state);
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
|
||||
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
|
||||
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
|
||||
var hash = md51_array(new Uint8Array(arr)), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
return SparkMD5;
|
||||
}));
|
||||
})(sparkMd5);
|
||||
var SparkMD5 = sparkMd5.exports;
|
||||
const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
|
||||
class FileChecksum {
|
||||
static create(file, callback) {
|
||||
const instance = new FileChecksum(file);
|
||||
instance.create(callback);
|
||||
}
|
||||
constructor(file) {
|
||||
this.file = file;
|
||||
this.chunkSize = 2097152;
|
||||
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
|
||||
this.chunkIndex = 0;
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.md5Buffer = new SparkMD5.ArrayBuffer;
|
||||
this.fileReader = new FileReader;
|
||||
this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event)));
|
||||
this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event)));
|
||||
this.readNextChunk();
|
||||
}
|
||||
fileReaderDidLoad(event) {
|
||||
this.md5Buffer.append(event.target.result);
|
||||
if (!this.readNextChunk()) {
|
||||
const binaryDigest = this.md5Buffer.end(true);
|
||||
const base64digest = btoa(binaryDigest);
|
||||
this.callback(null, base64digest);
|
||||
}
|
||||
}
|
||||
fileReaderDidError(event) {
|
||||
this.callback(`Error reading ${this.file.name}`);
|
||||
}
|
||||
readNextChunk() {
|
||||
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
|
||||
const start = this.chunkIndex * this.chunkSize;
|
||||
const end = Math.min(start + this.chunkSize, this.file.size);
|
||||
const bytes = fileSlice.call(this.file, start, end);
|
||||
this.fileReader.readAsArrayBuffer(bytes);
|
||||
this.chunkIndex++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
function getMetaValue(name) {
|
||||
const element = findElement(document.head, `meta[name="${name}"]`);
|
||||
if (element) {
|
||||
return element.getAttribute("content");
|
||||
}
|
||||
}
|
||||
function findElements(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
const elements = root.querySelectorAll(selector);
|
||||
return toArray(elements);
|
||||
}
|
||||
function findElement(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
return root.querySelector(selector);
|
||||
}
|
||||
function dispatchEvent(element, type, eventInit = {}) {
|
||||
const {disabled: disabled} = element;
|
||||
const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit;
|
||||
const event = document.createEvent("Event");
|
||||
event.initEvent(type, bubbles || true, cancelable || true);
|
||||
event.detail = detail || {};
|
||||
try {
|
||||
element.disabled = false;
|
||||
element.dispatchEvent(event);
|
||||
} finally {
|
||||
element.disabled = disabled;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
function toArray(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
} else if (Array.from) {
|
||||
return Array.from(value);
|
||||
} else {
|
||||
return [].slice.call(value);
|
||||
}
|
||||
}
|
||||
class BlobRecord {
|
||||
constructor(file, checksum, url, customHeaders = {}) {
|
||||
this.file = file;
|
||||
this.attributes = {
|
||||
filename: file.name,
|
||||
content_type: file.type || "application/octet-stream",
|
||||
byte_size: file.size,
|
||||
checksum: checksum
|
||||
};
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("POST", url, true);
|
||||
this.xhr.responseType = "json";
|
||||
this.xhr.setRequestHeader("Content-Type", "application/json");
|
||||
this.xhr.setRequestHeader("Accept", "application/json");
|
||||
this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
Object.keys(customHeaders).forEach((headerKey => {
|
||||
this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]);
|
||||
}));
|
||||
const csrfToken = getMetaValue("csrf-token");
|
||||
if (csrfToken != undefined) {
|
||||
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
get status() {
|
||||
return this.xhr.status;
|
||||
}
|
||||
get response() {
|
||||
const {responseType: responseType, response: response} = this.xhr;
|
||||
if (responseType == "json") {
|
||||
return response;
|
||||
} else {
|
||||
return JSON.parse(response);
|
||||
}
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(JSON.stringify({
|
||||
blob: this.attributes
|
||||
}));
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
if (this.status >= 200 && this.status < 300) {
|
||||
const {response: response} = this;
|
||||
const {direct_upload: direct_upload} = response;
|
||||
delete response.direct_upload;
|
||||
this.attributes = response;
|
||||
this.directUploadData = direct_upload;
|
||||
this.callback(null, this.toJSON());
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`);
|
||||
}
|
||||
toJSON() {
|
||||
const result = {};
|
||||
for (const key in this.attributes) {
|
||||
result[key] = this.attributes[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
class BlobUpload {
|
||||
constructor(blob) {
|
||||
this.blob = blob;
|
||||
this.file = blob.file;
|
||||
const {url: url, headers: headers} = blob.directUploadData;
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("PUT", url, true);
|
||||
this.xhr.responseType = "text";
|
||||
for (const key in headers) {
|
||||
this.xhr.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(this.file.slice());
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
const {status: status, response: response} = this.xhr;
|
||||
if (status >= 200 && status < 300) {
|
||||
this.callback(null, response);
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
|
||||
}
|
||||
}
|
||||
let id = 0;
|
||||
class DirectUpload {
|
||||
constructor(file, url, delegate, customHeaders = {}) {
|
||||
this.id = ++id;
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
this.delegate = delegate;
|
||||
this.customHeaders = customHeaders;
|
||||
}
|
||||
create(callback) {
|
||||
FileChecksum.create(this.file, ((error, checksum) => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders);
|
||||
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
||||
blob.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
const upload = new BlobUpload(blob);
|
||||
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
|
||||
upload.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
callback(null, blob.toJSON());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
function notify(object, methodName, ...messages) {
|
||||
if (object && typeof object[methodName] == "function") {
|
||||
return object[methodName](...messages);
|
||||
}
|
||||
}
|
||||
class DirectUploadController {
|
||||
constructor(input, file) {
|
||||
this.input = input;
|
||||
this.file = file;
|
||||
this.directUpload = new DirectUpload(this.file, this.url, this);
|
||||
this.dispatch("initialize");
|
||||
}
|
||||
start(callback) {
|
||||
const hiddenInput = document.createElement("input");
|
||||
hiddenInput.type = "hidden";
|
||||
hiddenInput.name = this.input.name;
|
||||
this.input.insertAdjacentElement("beforebegin", hiddenInput);
|
||||
this.dispatch("start");
|
||||
this.directUpload.create(((error, attributes) => {
|
||||
if (error) {
|
||||
hiddenInput.parentNode.removeChild(hiddenInput);
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
hiddenInput.value = attributes.signed_id;
|
||||
}
|
||||
this.dispatch("end");
|
||||
callback(error);
|
||||
}));
|
||||
}
|
||||
uploadRequestDidProgress(event) {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}
|
||||
get url() {
|
||||
return this.input.getAttribute("data-direct-upload-url");
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.file = this.file;
|
||||
detail.id = this.directUpload.id;
|
||||
return dispatchEvent(this.input, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
directUploadWillCreateBlobWithXHR(xhr) {
|
||||
this.dispatch("before-blob-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
this.dispatch("before-storage-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
||||
}
|
||||
}
|
||||
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
||||
class DirectUploadsController {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
this.inputs = findElements(form, inputSelector).filter((input => input.files.length));
|
||||
}
|
||||
start(callback) {
|
||||
const controllers = this.createDirectUploadControllers();
|
||||
const startNextController = () => {
|
||||
const controller = controllers.shift();
|
||||
if (controller) {
|
||||
controller.start((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
this.dispatch("end");
|
||||
} else {
|
||||
startNextController();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
callback();
|
||||
this.dispatch("end");
|
||||
}
|
||||
};
|
||||
this.dispatch("start");
|
||||
startNextController();
|
||||
}
|
||||
createDirectUploadControllers() {
|
||||
const controllers = [];
|
||||
this.inputs.forEach((input => {
|
||||
toArray(input.files).forEach((file => {
|
||||
const controller = new DirectUploadController(input, file);
|
||||
controllers.push(controller);
|
||||
}));
|
||||
}));
|
||||
return controllers;
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
return dispatchEvent(this.form, `direct-uploads:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
}
|
||||
const processingAttribute = "data-direct-uploads-processing";
|
||||
const submitButtonsByForm = new WeakMap;
|
||||
let started = false;
|
||||
function start() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
document.addEventListener("click", didClick, true);
|
||||
document.addEventListener("submit", didSubmitForm, true);
|
||||
document.addEventListener("ajax:before", didSubmitRemoteElement);
|
||||
}
|
||||
}
|
||||
function didClick(event) {
|
||||
const button = event.target.closest("button, input");
|
||||
if (button && button.type === "submit" && button.form) {
|
||||
submitButtonsByForm.set(button.form, button);
|
||||
}
|
||||
}
|
||||
function didSubmitForm(event) {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
function didSubmitRemoteElement(event) {
|
||||
if (event.target.tagName == "FORM") {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
}
|
||||
function handleFormSubmissionEvent(event) {
|
||||
const form = event.target;
|
||||
if (form.hasAttribute(processingAttribute)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
const controller = new DirectUploadsController(form);
|
||||
const {inputs: inputs} = controller;
|
||||
if (inputs.length) {
|
||||
event.preventDefault();
|
||||
form.setAttribute(processingAttribute, "");
|
||||
inputs.forEach(disable);
|
||||
controller.start((error => {
|
||||
form.removeAttribute(processingAttribute);
|
||||
if (error) {
|
||||
inputs.forEach(enable);
|
||||
} else {
|
||||
submitForm(form);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
function submitForm(form) {
|
||||
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
|
||||
if (button) {
|
||||
const {disabled: disabled} = button;
|
||||
button.disabled = false;
|
||||
button.focus();
|
||||
button.click();
|
||||
button.disabled = disabled;
|
||||
} else {
|
||||
button = document.createElement("input");
|
||||
button.type = "submit";
|
||||
button.style.display = "none";
|
||||
form.appendChild(button);
|
||||
button.click();
|
||||
form.removeChild(button);
|
||||
}
|
||||
submitButtonsByForm.delete(form);
|
||||
}
|
||||
function disable(input) {
|
||||
input.disabled = true;
|
||||
}
|
||||
function enable(input) {
|
||||
input.disabled = false;
|
||||
}
|
||||
function autostart() {
|
||||
if (window.ActiveStorage) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
setTimeout(autostart, 1);
|
||||
exports.DirectUpload = DirectUpload;
|
||||
exports.DirectUploadController = DirectUploadController;
|
||||
exports.DirectUploadsController = DirectUploadsController;
|
||||
exports.dispatchEvent = dispatchEvent;
|
||||
exports.start = start;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
}));
|
||||
Binary file not shown.
@@ -1,848 +0,0 @@
|
||||
var sparkMd5 = {
|
||||
exports: {}
|
||||
};
|
||||
|
||||
(function(module, exports) {
|
||||
(function(factory) {
|
||||
{
|
||||
module.exports = factory();
|
||||
}
|
||||
})((function(undefined$1) {
|
||||
var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ];
|
||||
function md5cycle(x, k) {
|
||||
var a = x[0], b = x[1], c = x[2], d = x[3];
|
||||
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[10] - 42063 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
|
||||
a = (a << 7 | a >>> 25) + b | 0;
|
||||
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
|
||||
d = (d << 12 | d >>> 20) + a | 0;
|
||||
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
|
||||
c = (c << 17 | c >>> 15) + d | 0;
|
||||
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
|
||||
b = (b << 22 | b >>> 10) + c | 0;
|
||||
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
|
||||
a = (a << 5 | a >>> 27) + b | 0;
|
||||
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
|
||||
d = (d << 9 | d >>> 23) + a | 0;
|
||||
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
|
||||
c = (c << 14 | c >>> 18) + d | 0;
|
||||
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
|
||||
b = (b << 20 | b >>> 12) + c | 0;
|
||||
a += (b ^ c ^ d) + k[5] - 378558 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
|
||||
a = (a << 4 | a >>> 28) + b | 0;
|
||||
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
|
||||
d = (d << 11 | d >>> 21) + a | 0;
|
||||
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
|
||||
c = (c << 16 | c >>> 16) + d | 0;
|
||||
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
|
||||
b = (b << 23 | b >>> 9) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
|
||||
a = (a << 6 | a >>> 26) + b | 0;
|
||||
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
|
||||
d = (d << 10 | d >>> 22) + a | 0;
|
||||
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
|
||||
c = (c << 15 | c >>> 17) + d | 0;
|
||||
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
|
||||
b = (b << 21 | b >>> 11) + c | 0;
|
||||
x[0] = a + x[0] | 0;
|
||||
x[1] = b + x[1] | 0;
|
||||
x[2] = c + x[2] | 0;
|
||||
x[3] = d + x[3] | 0;
|
||||
}
|
||||
function md5blk(s) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md5blk_array(a) {
|
||||
var md5blks = [], i;
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
function md51(s) {
|
||||
var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
||||
}
|
||||
s = s.substring(i - 64);
|
||||
length = s.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function md51_array(a) {
|
||||
var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
|
||||
}
|
||||
a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0);
|
||||
length = a.length;
|
||||
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= a[i] << (i % 4 << 3);
|
||||
}
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = n * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(state, tail);
|
||||
return state;
|
||||
}
|
||||
function rhex(n) {
|
||||
var s = "", j;
|
||||
for (j = 0; j < 4; j += 1) {
|
||||
s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function hex(x) {
|
||||
var i;
|
||||
for (i = 0; i < x.length; i += 1) {
|
||||
x[i] = rhex(x[i]);
|
||||
}
|
||||
return x.join("");
|
||||
}
|
||||
if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ;
|
||||
if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) {
|
||||
(function() {
|
||||
function clamp(val, length) {
|
||||
val = val | 0 || 0;
|
||||
if (val < 0) {
|
||||
return Math.max(val + length, 0);
|
||||
}
|
||||
return Math.min(val, length);
|
||||
}
|
||||
ArrayBuffer.prototype.slice = function(from, to) {
|
||||
var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray;
|
||||
if (to !== undefined$1) {
|
||||
end = clamp(to, length);
|
||||
}
|
||||
if (begin > end) {
|
||||
return new ArrayBuffer(0);
|
||||
}
|
||||
num = end - begin;
|
||||
target = new ArrayBuffer(num);
|
||||
targetArray = new Uint8Array(target);
|
||||
sourceArray = new Uint8Array(this, begin, num);
|
||||
targetArray.set(sourceArray);
|
||||
return target;
|
||||
};
|
||||
})();
|
||||
}
|
||||
function toUtf8(str) {
|
||||
if (/[\u0080-\uFFFF]/.test(str)) {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
||||
var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
arr[i] = str.charCodeAt(i);
|
||||
}
|
||||
return returnUInt8Array ? arr : buff;
|
||||
}
|
||||
function arrayBuffer2Utf8Str(buff) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buff));
|
||||
}
|
||||
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
||||
var result = new Uint8Array(first.byteLength + second.byteLength);
|
||||
result.set(new Uint8Array(first));
|
||||
result.set(new Uint8Array(second), first.byteLength);
|
||||
return returnUInt8Array ? result : result.buffer;
|
||||
}
|
||||
function hexToBinaryString(hex) {
|
||||
var bytes = [], length = hex.length, x;
|
||||
for (x = 0; x < length - 1; x += 2) {
|
||||
bytes.push(parseInt(hex.substr(x, 2), 16));
|
||||
}
|
||||
return String.fromCharCode.apply(String, bytes);
|
||||
}
|
||||
function SparkMD5() {
|
||||
this.reset();
|
||||
}
|
||||
SparkMD5.prototype.append = function(str) {
|
||||
this.appendBinary(toUtf8(str));
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.appendBinary = function(contents) {
|
||||
this._buff += contents;
|
||||
this._length += contents.length;
|
||||
var length = this._buff.length, i;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
|
||||
}
|
||||
this._buff = this._buff.substring(i - 64);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.prototype.reset = function() {
|
||||
this._buff = "";
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.getState = function() {
|
||||
return {
|
||||
buff: this._buff,
|
||||
length: this._length,
|
||||
hash: this._hash.slice()
|
||||
};
|
||||
};
|
||||
SparkMD5.prototype.setState = function(state) {
|
||||
this._buff = state.buff;
|
||||
this._length = state.length;
|
||||
this._hash = state.hash;
|
||||
return this;
|
||||
};
|
||||
SparkMD5.prototype.destroy = function() {
|
||||
delete this._hash;
|
||||
delete this._buff;
|
||||
delete this._length;
|
||||
};
|
||||
SparkMD5.prototype._finish = function(tail, length) {
|
||||
var i = length, tmp, lo, hi;
|
||||
tail[i >> 2] |= 128 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(this._hash, tail);
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0;
|
||||
}
|
||||
}
|
||||
tmp = this._length * 8;
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
||||
lo = parseInt(tmp[2], 16);
|
||||
hi = parseInt(tmp[1], 16) || 0;
|
||||
tail[14] = lo;
|
||||
tail[15] = hi;
|
||||
md5cycle(this._hash, tail);
|
||||
};
|
||||
SparkMD5.hash = function(str, raw) {
|
||||
return SparkMD5.hashBinary(toUtf8(str), raw);
|
||||
};
|
||||
SparkMD5.hashBinary = function(content, raw) {
|
||||
var hash = md51(content), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer = function() {
|
||||
this.reset();
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
|
||||
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i;
|
||||
this._length += arr.byteLength;
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
|
||||
}
|
||||
this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
|
||||
var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff[i] << (i % 4 << 3);
|
||||
}
|
||||
this._finish(tail, length);
|
||||
ret = hex(this._hash);
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret);
|
||||
}
|
||||
this.reset();
|
||||
return ret;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.reset = function() {
|
||||
this._buff = new Uint8Array(0);
|
||||
this._length = 0;
|
||||
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
||||
return this;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.getState = function() {
|
||||
var state = SparkMD5.prototype.getState.call(this);
|
||||
state.buff = arrayBuffer2Utf8Str(state.buff);
|
||||
return state;
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
|
||||
state.buff = utf8Str2ArrayBuffer(state.buff, true);
|
||||
return SparkMD5.prototype.setState.call(this, state);
|
||||
};
|
||||
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
|
||||
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
|
||||
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
|
||||
var hash = md51_array(new Uint8Array(arr)), ret = hex(hash);
|
||||
return raw ? hexToBinaryString(ret) : ret;
|
||||
};
|
||||
return SparkMD5;
|
||||
}));
|
||||
})(sparkMd5);
|
||||
|
||||
var SparkMD5 = sparkMd5.exports;
|
||||
|
||||
const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
|
||||
|
||||
class FileChecksum {
|
||||
static create(file, callback) {
|
||||
const instance = new FileChecksum(file);
|
||||
instance.create(callback);
|
||||
}
|
||||
constructor(file) {
|
||||
this.file = file;
|
||||
this.chunkSize = 2097152;
|
||||
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
|
||||
this.chunkIndex = 0;
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.md5Buffer = new SparkMD5.ArrayBuffer;
|
||||
this.fileReader = new FileReader;
|
||||
this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event)));
|
||||
this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event)));
|
||||
this.readNextChunk();
|
||||
}
|
||||
fileReaderDidLoad(event) {
|
||||
this.md5Buffer.append(event.target.result);
|
||||
if (!this.readNextChunk()) {
|
||||
const binaryDigest = this.md5Buffer.end(true);
|
||||
const base64digest = btoa(binaryDigest);
|
||||
this.callback(null, base64digest);
|
||||
}
|
||||
}
|
||||
fileReaderDidError(event) {
|
||||
this.callback(`Error reading ${this.file.name}`);
|
||||
}
|
||||
readNextChunk() {
|
||||
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
|
||||
const start = this.chunkIndex * this.chunkSize;
|
||||
const end = Math.min(start + this.chunkSize, this.file.size);
|
||||
const bytes = fileSlice.call(this.file, start, end);
|
||||
this.fileReader.readAsArrayBuffer(bytes);
|
||||
this.chunkIndex++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMetaValue(name) {
|
||||
const element = findElement(document.head, `meta[name="${name}"]`);
|
||||
if (element) {
|
||||
return element.getAttribute("content");
|
||||
}
|
||||
}
|
||||
|
||||
function findElements(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
const elements = root.querySelectorAll(selector);
|
||||
return toArray(elements);
|
||||
}
|
||||
|
||||
function findElement(root, selector) {
|
||||
if (typeof root == "string") {
|
||||
selector = root;
|
||||
root = document;
|
||||
}
|
||||
return root.querySelector(selector);
|
||||
}
|
||||
|
||||
function dispatchEvent(element, type, eventInit = {}) {
|
||||
const {disabled: disabled} = element;
|
||||
const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit;
|
||||
const event = document.createEvent("Event");
|
||||
event.initEvent(type, bubbles || true, cancelable || true);
|
||||
event.detail = detail || {};
|
||||
try {
|
||||
element.disabled = false;
|
||||
element.dispatchEvent(event);
|
||||
} finally {
|
||||
element.disabled = disabled;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
function toArray(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
} else if (Array.from) {
|
||||
return Array.from(value);
|
||||
} else {
|
||||
return [].slice.call(value);
|
||||
}
|
||||
}
|
||||
|
||||
class BlobRecord {
|
||||
constructor(file, checksum, url, customHeaders = {}) {
|
||||
this.file = file;
|
||||
this.attributes = {
|
||||
filename: file.name,
|
||||
content_type: file.type || "application/octet-stream",
|
||||
byte_size: file.size,
|
||||
checksum: checksum
|
||||
};
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("POST", url, true);
|
||||
this.xhr.responseType = "json";
|
||||
this.xhr.setRequestHeader("Content-Type", "application/json");
|
||||
this.xhr.setRequestHeader("Accept", "application/json");
|
||||
this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
Object.keys(customHeaders).forEach((headerKey => {
|
||||
this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]);
|
||||
}));
|
||||
const csrfToken = getMetaValue("csrf-token");
|
||||
if (csrfToken != undefined) {
|
||||
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
get status() {
|
||||
return this.xhr.status;
|
||||
}
|
||||
get response() {
|
||||
const {responseType: responseType, response: response} = this.xhr;
|
||||
if (responseType == "json") {
|
||||
return response;
|
||||
} else {
|
||||
return JSON.parse(response);
|
||||
}
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(JSON.stringify({
|
||||
blob: this.attributes
|
||||
}));
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
if (this.status >= 200 && this.status < 300) {
|
||||
const {response: response} = this;
|
||||
const {direct_upload: direct_upload} = response;
|
||||
delete response.direct_upload;
|
||||
this.attributes = response;
|
||||
this.directUploadData = direct_upload;
|
||||
this.callback(null, this.toJSON());
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`);
|
||||
}
|
||||
toJSON() {
|
||||
const result = {};
|
||||
for (const key in this.attributes) {
|
||||
result[key] = this.attributes[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class BlobUpload {
|
||||
constructor(blob) {
|
||||
this.blob = blob;
|
||||
this.file = blob.file;
|
||||
const {url: url, headers: headers} = blob.directUploadData;
|
||||
this.xhr = new XMLHttpRequest;
|
||||
this.xhr.open("PUT", url, true);
|
||||
this.xhr.responseType = "text";
|
||||
for (const key in headers) {
|
||||
this.xhr.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
||||
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
||||
}
|
||||
create(callback) {
|
||||
this.callback = callback;
|
||||
this.xhr.send(this.file.slice());
|
||||
}
|
||||
requestDidLoad(event) {
|
||||
const {status: status, response: response} = this.xhr;
|
||||
if (status >= 200 && status < 300) {
|
||||
this.callback(null, response);
|
||||
} else {
|
||||
this.requestDidError(event);
|
||||
}
|
||||
}
|
||||
requestDidError(event) {
|
||||
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
let id = 0;
|
||||
|
||||
class DirectUpload {
|
||||
constructor(file, url, delegate, customHeaders = {}) {
|
||||
this.id = ++id;
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
this.delegate = delegate;
|
||||
this.customHeaders = customHeaders;
|
||||
}
|
||||
create(callback) {
|
||||
FileChecksum.create(this.file, ((error, checksum) => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders);
|
||||
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
||||
blob.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
const upload = new BlobUpload(blob);
|
||||
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
|
||||
upload.create((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
callback(null, blob.toJSON());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function notify(object, methodName, ...messages) {
|
||||
if (object && typeof object[methodName] == "function") {
|
||||
return object[methodName](...messages);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectUploadController {
|
||||
constructor(input, file) {
|
||||
this.input = input;
|
||||
this.file = file;
|
||||
this.directUpload = new DirectUpload(this.file, this.url, this);
|
||||
this.dispatch("initialize");
|
||||
}
|
||||
start(callback) {
|
||||
const hiddenInput = document.createElement("input");
|
||||
hiddenInput.type = "hidden";
|
||||
hiddenInput.name = this.input.name;
|
||||
this.input.insertAdjacentElement("beforebegin", hiddenInput);
|
||||
this.dispatch("start");
|
||||
this.directUpload.create(((error, attributes) => {
|
||||
if (error) {
|
||||
hiddenInput.parentNode.removeChild(hiddenInput);
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
hiddenInput.value = attributes.signed_id;
|
||||
}
|
||||
this.dispatch("end");
|
||||
callback(error);
|
||||
}));
|
||||
}
|
||||
uploadRequestDidProgress(event) {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}
|
||||
get url() {
|
||||
return this.input.getAttribute("data-direct-upload-url");
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.file = this.file;
|
||||
detail.id = this.directUpload.id;
|
||||
return dispatchEvent(this.input, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
directUploadWillCreateBlobWithXHR(xhr) {
|
||||
this.dispatch("before-blob-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
this.dispatch("before-storage-request", {
|
||||
xhr: xhr
|
||||
});
|
||||
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
||||
}
|
||||
}
|
||||
|
||||
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
||||
|
||||
class DirectUploadsController {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
this.inputs = findElements(form, inputSelector).filter((input => input.files.length));
|
||||
}
|
||||
start(callback) {
|
||||
const controllers = this.createDirectUploadControllers();
|
||||
const startNextController = () => {
|
||||
const controller = controllers.shift();
|
||||
if (controller) {
|
||||
controller.start((error => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
this.dispatch("end");
|
||||
} else {
|
||||
startNextController();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
callback();
|
||||
this.dispatch("end");
|
||||
}
|
||||
};
|
||||
this.dispatch("start");
|
||||
startNextController();
|
||||
}
|
||||
createDirectUploadControllers() {
|
||||
const controllers = [];
|
||||
this.inputs.forEach((input => {
|
||||
toArray(input.files).forEach((file => {
|
||||
const controller = new DirectUploadController(input, file);
|
||||
controllers.push(controller);
|
||||
}));
|
||||
}));
|
||||
return controllers;
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
return dispatchEvent(this.form, `direct-uploads:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const processingAttribute = "data-direct-uploads-processing";
|
||||
|
||||
const submitButtonsByForm = new WeakMap;
|
||||
|
||||
let started = false;
|
||||
|
||||
function start() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
document.addEventListener("click", didClick, true);
|
||||
document.addEventListener("submit", didSubmitForm, true);
|
||||
document.addEventListener("ajax:before", didSubmitRemoteElement);
|
||||
}
|
||||
}
|
||||
|
||||
function didClick(event) {
|
||||
const button = event.target.closest("button, input");
|
||||
if (button && button.type === "submit" && button.form) {
|
||||
submitButtonsByForm.set(button.form, button);
|
||||
}
|
||||
}
|
||||
|
||||
function didSubmitForm(event) {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
|
||||
function didSubmitRemoteElement(event) {
|
||||
if (event.target.tagName == "FORM") {
|
||||
handleFormSubmissionEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function handleFormSubmissionEvent(event) {
|
||||
const form = event.target;
|
||||
if (form.hasAttribute(processingAttribute)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
const controller = new DirectUploadsController(form);
|
||||
const {inputs: inputs} = controller;
|
||||
if (inputs.length) {
|
||||
event.preventDefault();
|
||||
form.setAttribute(processingAttribute, "");
|
||||
inputs.forEach(disable);
|
||||
controller.start((error => {
|
||||
form.removeAttribute(processingAttribute);
|
||||
if (error) {
|
||||
inputs.forEach(enable);
|
||||
} else {
|
||||
submitForm(form);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function submitForm(form) {
|
||||
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
|
||||
if (button) {
|
||||
const {disabled: disabled} = button;
|
||||
button.disabled = false;
|
||||
button.focus();
|
||||
button.click();
|
||||
button.disabled = disabled;
|
||||
} else {
|
||||
button = document.createElement("input");
|
||||
button.type = "submit";
|
||||
button.style.display = "none";
|
||||
form.appendChild(button);
|
||||
button.click();
|
||||
form.removeChild(button);
|
||||
}
|
||||
submitButtonsByForm.delete(form);
|
||||
}
|
||||
|
||||
function disable(input) {
|
||||
input.disabled = true;
|
||||
}
|
||||
|
||||
function enable(input) {
|
||||
input.disabled = false;
|
||||
}
|
||||
|
||||
function autostart() {
|
||||
if (window.ActiveStorage) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(autostart, 1);
|
||||
|
||||
export { DirectUpload, DirectUploadController, DirectUploadsController, dispatchEvent, start };
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Admin controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
Place all the styles related to the matching controller here.
|
||||
They will automatically be included in application.css.
|
||||
*/
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,17 +0,0 @@
|
||||
.navbar-inverse.navbar-fixed-top {
|
||||
z-index: 1040; /* Ensure main navbar is above tournament navbar */
|
||||
}
|
||||
|
||||
#tournament-navbar {
|
||||
top: 50px; /* Position below the first fixed navbar */
|
||||
z-index: 1030; /* Explicitly set standard fixed navbar z-index */
|
||||
}
|
||||
|
||||
/* Make desktop navbar dropdowns scrollable if they overflow */
|
||||
@media (min-width: 768px) {
|
||||
/* Target dropdowns in main nav and tournament nav specifically */
|
||||
.navbar-fixed-top .dropdown-menu {
|
||||
max-height: 70vh; /* Adjust as needed - 70% of viewport height */
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Matches controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Mats controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
@@ -1,69 +0,0 @@
|
||||
body {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
&:visited {
|
||||
color: #666;
|
||||
}
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
&.field, &.actions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#notice {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.field_with_errors {
|
||||
padding: 2px;
|
||||
background-color: red;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#error_explanation {
|
||||
width: 450px;
|
||||
border: 2px solid red;
|
||||
padding: 7px;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 20px;
|
||||
background-color: #f0f0f0;
|
||||
h2 {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
padding: 5px 5px 5px 15px;
|
||||
font-size: 12px;
|
||||
margin: -7px;
|
||||
margin-bottom: 0px;
|
||||
background-color: #c00;
|
||||
color: #fff;
|
||||
}
|
||||
ul li {
|
||||
font-size: 12px;
|
||||
list-style: square;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the Schools controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the StaticPages controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
// Place all the styles related to the tournaments controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
Binary file not shown.
@@ -1,470 +0,0 @@
|
||||
|
||||
trix-editor {
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 3px;
|
||||
margin: 0;
|
||||
padding: 0.4em 0.6em;
|
||||
min-height: 5em;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
trix-toolbar * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
trix-toolbar .trix-button-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
overflow-x: auto;
|
||||
}
|
||||
trix-toolbar .trix-button-group {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #bbb;
|
||||
border-top-color: #ccc;
|
||||
border-bottom-color: #888;
|
||||
border-radius: 3px;
|
||||
}
|
||||
trix-toolbar .trix-button-group:not(:first-child) {
|
||||
margin-left: 1.5vw;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
trix-toolbar .trix-button-group:not(:first-child) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
trix-toolbar .trix-button-group-spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
trix-toolbar .trix-button-group-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
trix-toolbar .trix-button {
|
||||
position: relative;
|
||||
float: left;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-size: 0.75em;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
padding: 0 0.5em;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
trix-toolbar .trix-button:not(:first-child) {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
trix-toolbar .trix-button.trix-active {
|
||||
background: #cbeefa;
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
trix-toolbar .trix-button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
trix-toolbar .trix-button:disabled {
|
||||
color: rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
trix-toolbar .trix-button {
|
||||
letter-spacing: -0.01em;
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
}
|
||||
trix-toolbar .trix-button--icon {
|
||||
font-size: inherit;
|
||||
width: 2.6em;
|
||||
height: 1.6em;
|
||||
max-width: calc(0.8em + 4vw);
|
||||
text-indent: -9999px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
trix-toolbar .trix-button--icon {
|
||||
height: 2em;
|
||||
max-width: calc(0.8em + 3.5vw);
|
||||
}
|
||||
}
|
||||
trix-toolbar .trix-button--icon::before {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
content: "";
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
trix-toolbar .trix-button--icon::before {
|
||||
right: 6%;
|
||||
left: 6%;
|
||||
}
|
||||
}
|
||||
trix-toolbar .trix-button--icon.trix-active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
trix-toolbar .trix-button--icon:disabled::before {
|
||||
opacity: 0.125;
|
||||
}
|
||||
trix-toolbar .trix-button--icon-attach::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10.5%2018V7.5c0-2.25%203-2.25%203%200V18c0%204.125-6%204.125-6%200V7.5c0-6.375%209-6.375%209%200V18%22%20stroke%3D%22%23000%22%20stroke-width%3D%222%22%20stroke-miterlimit%3D%2210%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
|
||||
top: 8%;
|
||||
bottom: 4%;
|
||||
}
|
||||
trix-toolbar .trix-button--icon-bold::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M6.522%2019.242a.5.5%200%200%201-.5-.5V5.35a.5.5%200%200%201%20.5-.5h5.783c1.347%200%202.46.345%203.24.982.783.64%201.216%201.562%201.216%202.683%200%201.13-.587%202.129-1.476%202.71a.35.35%200%200%200%20.049.613c1.259.56%202.101%201.742%202.101%203.22%200%201.282-.483%202.334-1.363%203.063-.876.726-2.132%201.12-3.66%201.12h-5.89ZM9.27%207.347v3.362h1.97c.766%200%201.347-.17%201.733-.464.38-.291.587-.716.587-1.27%200-.53-.183-.928-.513-1.198-.334-.273-.838-.43-1.505-.43H9.27Zm0%205.606v3.791h2.389c.832%200%201.448-.177%201.853-.497.399-.315.614-.786.614-1.423%200-.62-.22-1.077-.63-1.385-.418-.313-1.053-.486-1.905-.486H9.27Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-italic::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M9%205h6.5v2h-2.23l-2.31%2010H13v2H6v-2h2.461l2.306-10H9V5Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-link::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M18.948%205.258a4.337%204.337%200%200%200-6.108%200L11.217%206.87a.993.993%200%200%200%200%201.41c.392.39%201.027.39%201.418%200l1.623-1.613a2.323%202.323%200%200%201%203.271%200%202.29%202.29%200%200%201%200%203.251l-2.393%202.38a3.021%203.021%200%200%201-4.255%200l-.05-.049a1.007%201.007%200%200%200-1.418%200%20.993.993%200%200%200%200%201.41l.05.049a5.036%205.036%200%200%200%207.091%200l2.394-2.38a4.275%204.275%200%200%200%200-6.072Zm-13.683%2013.6a4.337%204.337%200%200%200%206.108%200l1.262-1.255a.993.993%200%200%200%200-1.41%201.007%201.007%200%200%200-1.418%200L9.954%2017.45a2.323%202.323%200%200%201-3.27%200%202.29%202.29%200%200%201%200-3.251l2.344-2.331a2.579%202.579%200%200%201%203.631%200c.392.39%201.027.39%201.419%200a.993.993%200%200%200%200-1.41%204.593%204.593%200%200%200-6.468%200l-2.345%202.33a4.275%204.275%200%200%200%200%206.072Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-strike::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M6%2014.986c.088%202.647%202.246%204.258%205.635%204.258%203.496%200%205.713-1.728%205.713-4.463%200-.275-.02-.536-.062-.781h-3.461c.398.293.573.654.573%201.123%200%201.035-1.074%201.787-2.646%201.787-1.563%200-2.773-.762-2.91-1.924H6ZM6.432%2010h3.763c-.632-.314-.914-.715-.914-1.273%200-1.045.977-1.739%202.432-1.739%201.475%200%202.52.723%202.617%201.914h2.764c-.05-2.548-2.11-4.238-5.39-4.238-3.145%200-5.392%201.719-5.392%204.316%200%20.363.04.703.12%201.02ZM4%2011a1%201%200%201%200%200%202h15a1%201%200%201%200%200-2H4Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-quote::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M4.581%208.471c.44-.5%201.056-.834%201.758-.995C8.074%207.17%209.201%207.822%2010%208.752c1.354%201.578%201.33%203.555.394%205.277-.941%201.731-2.788%203.163-4.988%203.56a.622.622%200%200%201-.653-.317c-.113-.205-.121-.49.16-.764.294-.286.567-.566.791-.835.222-.266.413-.54.524-.815.113-.28.156-.597.026-.908-.128-.303-.39-.524-.72-.69a3.02%203.02%200%200%201-1.674-2.7c0-.905.283-1.59.72-2.088Zm9.419%200c.44-.5%201.055-.834%201.758-.995%201.734-.306%202.862.346%203.66%201.276%201.355%201.578%201.33%203.555.395%205.277-.941%201.731-2.789%203.163-4.988%203.56a.622.622%200%200%201-.653-.317c-.113-.205-.122-.49.16-.764.294-.286.567-.566.791-.835.222-.266.412-.54.523-.815.114-.28.157-.597.026-.908-.127-.303-.39-.524-.72-.69a3.02%203.02%200%200%201-1.672-2.701c0-.905.283-1.59.72-2.088Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-heading-1::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M21.5%207.5v-3h-12v3H14v13h3v-13h4.5ZM9%2013.5h3.5v-3h-10v3H6v7h3v-7Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-code::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M3.293%2011.293a1%201%200%200%200%200%201.414l4%204a1%201%200%201%200%201.414-1.414L5.414%2012l3.293-3.293a1%201%200%200%200-1.414-1.414l-4%204Zm13.414%205.414%204-4a1%201%200%200%200%200-1.414l-4-4a1%201%200%201%200-1.414%201.414L18.586%2012l-3.293%203.293a1%201%200%200%200%201.414%201.414Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-bullet-list::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M5%207.5a1.5%201.5%200%201%200%200-3%201.5%201.5%200%200%200%200%203ZM8%206a1%201%200%200%201%201-1h11a1%201%200%201%201%200%202H9a1%201%200%200%201-1-1Zm1%205a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm0%206a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm-2.5-5a1.5%201.5%200%201%201-3%200%201.5%201.5%200%200%201%203%200ZM5%2019.5a1.5%201.5%200%201%200%200-3%201.5%201.5%200%200%200%200%203Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-number-list::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M3%204h2v4H4V5H3V4Zm5%202a1%201%200%200%201%201-1h11a1%201%200%201%201%200%202H9a1%201%200%200%201-1-1Zm1%205a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm0%206a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm-3.5-7H6v1l-1.5%202H6v1H3v-1l1.667-2H3v-1h2.5ZM3%2017v-1h3v4H3v-1h2v-.5H4v-1h1V17H3Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-undo::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M3%2014a1%201%200%200%200%201%201h6a1%201%200%201%200%200-2H6.257c2.247-2.764%205.151-3.668%207.579-3.264%202.589.432%204.739%202.356%205.174%205.405a1%201%200%200%200%201.98-.283c-.564-3.95-3.415-6.526-6.825-7.095C11.084%207.25%207.63%208.377%205%2011.39V8a1%201%200%200%200-2%200v6Zm2-1Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-redo::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M21%2014a1%201%200%200%201-1%201h-6a1%201%200%201%201%200-2h3.743c-2.247-2.764-5.151-3.668-7.579-3.264-2.589.432-4.739%202.356-5.174%205.405a1%201%200%200%201-1.98-.283c.564-3.95%203.415-6.526%206.826-7.095%203.08-.513%206.534.614%209.164%203.626V8a1%201%200%201%201%202%200v6Zm-2-1Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-decrease-nesting-level::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M5%206a1%201%200%200%201%201-1h12a1%201%200%201%201%200%202H6a1%201%200%200%201-1-1Zm4%205a1%201%200%201%200%200%202h9a1%201%200%201%200%200-2H9Zm-3%206a1%201%200%201%200%200%202h12a1%201%200%201%200%200-2H6Zm-3.707-5.707a1%201%200%200%200%200%201.414l2%202a1%201%200%201%200%201.414-1.414L4.414%2012l1.293-1.293a1%201%200%200%200-1.414-1.414l-2%202Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-button--icon-increase-nesting-level::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M5%206a1%201%200%200%201%201-1h12a1%201%200%201%201%200%202H6a1%201%200%200%201-1-1Zm4%205a1%201%200%201%200%200%202h9a1%201%200%201%200%200-2H9Zm-3%206a1%201%200%201%200%200%202h12a1%201%200%201%200%200-2H6Zm-2.293-2.293%202-2a1%201%200%200%200%200-1.414l-2-2a1%201%200%201%200-1.414%201.414L3.586%2012l-1.293%201.293a1%201%200%201%200%201.414%201.414Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
trix-toolbar .trix-dialogs {
|
||||
position: relative;
|
||||
}
|
||||
trix-toolbar .trix-dialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
font-size: 0.75em;
|
||||
padding: 15px 10px;
|
||||
background: #fff;
|
||||
box-shadow: 0 0.3em 1em #ccc;
|
||||
border-top: 2px solid #888;
|
||||
border-radius: 5px;
|
||||
z-index: 5;
|
||||
}
|
||||
trix-toolbar .trix-input--dialog {
|
||||
font-size: inherit;
|
||||
font-weight: normal;
|
||||
padding: 0.5em 0.8em;
|
||||
margin: 0 10px 0 0;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #bbb;
|
||||
background-color: #fff;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
trix-toolbar .trix-input--dialog.validate:invalid {
|
||||
box-shadow: #F00 0px 0px 1.5px 1px;
|
||||
}
|
||||
trix-toolbar .trix-button--dialog {
|
||||
font-size: inherit;
|
||||
padding: 0.5em;
|
||||
border-bottom: none;
|
||||
}
|
||||
trix-toolbar .trix-dialog--link {
|
||||
max-width: 600px;
|
||||
}
|
||||
trix-toolbar .trix-dialog__link-fields {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
trix-toolbar .trix-dialog__link-fields .trix-input {
|
||||
flex: 1;
|
||||
}
|
||||
trix-toolbar .trix-dialog__link-fields .trix-button-group {
|
||||
flex: 0 0 content;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable]:not(.attachment__caption-editor) {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable] ::-moz-selection, trix-editor [data-trix-mutable]::-moz-selection,
|
||||
trix-editor [data-trix-cursor-target]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
trix-editor [data-trix-mutable] ::selection, trix-editor [data-trix-mutable]::selection,
|
||||
trix-editor [data-trix-cursor-target]::selection {
|
||||
background: none;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable].attachment__caption-editor:focus::-moz-selection {
|
||||
background: highlight;
|
||||
}
|
||||
trix-editor [data-trix-mutable].attachment__caption-editor:focus::selection {
|
||||
background: highlight;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable].attachment.attachment--file {
|
||||
box-shadow: 0 0 0 2px highlight;
|
||||
border-color: transparent;
|
||||
}
|
||||
trix-editor [data-trix-mutable].attachment img {
|
||||
box-shadow: 0 0 0 2px highlight;
|
||||
}
|
||||
trix-editor .attachment {
|
||||
position: relative;
|
||||
}
|
||||
trix-editor .attachment:hover {
|
||||
cursor: default;
|
||||
}
|
||||
trix-editor .attachment--preview .attachment__caption:hover {
|
||||
cursor: text;
|
||||
}
|
||||
trix-editor .attachment__progress {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
height: 20px;
|
||||
top: calc(50% - 10px);
|
||||
left: 5%;
|
||||
width: 90%;
|
||||
opacity: 0.9;
|
||||
transition: opacity 200ms ease-in;
|
||||
}
|
||||
trix-editor .attachment__progress[value="100"] {
|
||||
opacity: 0;
|
||||
}
|
||||
trix-editor .attachment__caption-editor {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
trix-editor .attachment__toolbar {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: -0.9em;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
trix-editor .trix-button-group {
|
||||
display: inline-flex;
|
||||
}
|
||||
trix-editor .trix-button {
|
||||
position: relative;
|
||||
float: left;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
font-size: 80%;
|
||||
padding: 0 0.8em;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
trix-editor .trix-button:not(:first-child) {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
trix-editor .trix-button.trix-active {
|
||||
background: #cbeefa;
|
||||
}
|
||||
trix-editor .trix-button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
trix-editor .trix-button--remove {
|
||||
text-indent: -9999px;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
line-height: 1.8em;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
border: 2px solid highlight;
|
||||
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
trix-editor .trix-button--remove::before {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0.7;
|
||||
content: "";
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20height%3D%2224%22%20width%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M19%206.41%2017.59%205%2012%2010.59%206.41%205%205%206.41%2010.59%2012%205%2017.59%206.41%2019%2012%2013.41%2017.59%2019%2019%2017.59%2013.41%2012z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 90%;
|
||||
}
|
||||
trix-editor .trix-button--remove:hover {
|
||||
border-color: #333;
|
||||
}
|
||||
trix-editor .trix-button--remove:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
trix-editor .attachment__metadata-container {
|
||||
position: relative;
|
||||
}
|
||||
trix-editor .attachment__metadata {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 2em;
|
||||
transform: translate(-50%, 0);
|
||||
max-width: 90%;
|
||||
padding: 0.1em 0.6em;
|
||||
font-size: 0.8em;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 3px;
|
||||
}
|
||||
trix-editor .attachment__metadata .attachment__name {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
trix-editor .attachment__metadata .attachment__size {
|
||||
margin-left: 0.2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.trix-content {
|
||||
line-height: 1.5;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
.trix-content * {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.trix-content h1 {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.trix-content blockquote {
|
||||
border: 0 solid #ccc;
|
||||
border-left-width: 0.3em;
|
||||
margin-left: 0.3em;
|
||||
padding-left: 0.6em;
|
||||
}
|
||||
.trix-content [dir=rtl] blockquote,
|
||||
.trix-content blockquote[dir=rtl] {
|
||||
border-width: 0;
|
||||
border-right-width: 0.3em;
|
||||
margin-right: 0.3em;
|
||||
padding-right: 0.6em;
|
||||
}
|
||||
.trix-content li {
|
||||
margin-left: 1em;
|
||||
}
|
||||
.trix-content [dir=rtl] li {
|
||||
margin-right: 1em;
|
||||
}
|
||||
.trix-content pre {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 0.5em;
|
||||
white-space: pre;
|
||||
background-color: #eee;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.trix-content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.trix-content .attachment {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
}
|
||||
.trix-content .attachment a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.trix-content .attachment a:hover, .trix-content .attachment a:visited:hover {
|
||||
color: inherit;
|
||||
}
|
||||
.trix-content .attachment__caption {
|
||||
text-align: center;
|
||||
}
|
||||
.trix-content .attachment__caption .attachment__name + .attachment__size::before {
|
||||
content: " •";
|
||||
}
|
||||
.trix-content .attachment--preview {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.trix-content .attachment--preview .attachment__caption {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.trix-content .attachment--file {
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
margin: 0 2px 2px 2px;
|
||||
padding: 0.4em 1em;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.trix-content .attachment-gallery {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
}
|
||||
.trix-content .attachment-gallery .attachment {
|
||||
flex: 1 0 33%;
|
||||
padding: 0 0.5em;
|
||||
max-width: 33%;
|
||||
}
|
||||
.trix-content .attachment-gallery.attachment-gallery--2 .attachment, .trix-content .attachment-gallery.attachment-gallery--4 .attachment {
|
||||
flex-basis: 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user