diff --git a/app/assets/javascripts/admin/trix_direct_upload.js b/app/assets/javascripts/admin/trix_direct_upload.js deleted file mode 100644 index f622fde380044ed1514be220a77e2adb234263b4..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/admin/trix_direct_upload.js +++ /dev/null @@ -1,20 +0,0 @@ -(function() { - - addEventListener("trix-attachment-add", function(event) { - var file = event.attachment.file; - if (file) { - var upload = new window.ActiveStorage.DirectUpload(file,'/rails/active_storage/direct_uploads', window); - upload.create((error, attributes) => { - if (error) { - return false; - } else { - return event.attachment.setAttributes({ - url: `/rails/active_storage/blobs/${attributes.signed_id}/${attributes.filename}`, - href: `/rails/active_storage/blobs/${attributes.signed_id}/${attributes.filename}`, - }); - } - }); - } - }) - -})(); diff --git a/app/assets/stylesheets/commons/_actiontext.sass b/app/assets/stylesheets/commons/_actiontext.sass index 3669d72572d4c5be3e2064f5daa85601f32ab15e..266498590207b3450a25522403ccd54e7ea1bdd4 100644 --- a/app/assets/stylesheets/commons/_actiontext.sass +++ b/app/assets/stylesheets/commons/_actiontext.sass @@ -17,6 +17,9 @@ h3 font-size: 1.15rem + .attachment + text-align: initial + .attachment-gallery > action-text-attachment, > .attachment diff --git a/app/controllers/active_storage/direct_uploads_controller.rb b/app/controllers/active_storage/direct_uploads_controller.rb deleted file mode 100644 index fe237d61410167e36036c91eed19c037b729974b..0000000000000000000000000000000000000000 --- a/app/controllers/active_storage/direct_uploads_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -# Creates a new blob on the server side in anticipation of a direct-to-service upload from the client side. -# When the client-side upload is completed, the signed_blob_id can be submitted as part of the form to reference -# the blob that was created up front. -class ActiveStorage::DirectUploadsController < ActiveStorage::BaseController - include ApplicationController::WithUniversity - - def create - blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_args) - blob.update_column(:university_id, current_university.id) - render json: direct_upload_json(blob) - end - - private - def blob_args - params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, metadata: {}).to_h.symbolize_keys - end - - def direct_upload_json(blob) - blob.as_json(root: false, methods: :signed_id).merge(direct_upload: { - url: blob.service_url_for_direct_upload, - headers: blob.service_headers_for_direct_upload - }) - end - -end diff --git a/app/models/research/journal/article.rb b/app/models/research/journal/article.rb index 20a415c655deaf08befa1c4a77cf15c2faa94b1f..0410c664d3b6e9859bf11e853a325e9043904631 100644 --- a/app/models/research/journal/article.rb +++ b/app/models/research/journal/article.rb @@ -10,7 +10,7 @@ # text :text # title :string # created_at :datetime not null -# updated_at :date not null +# updated_at :datetime not null # research_journal_id :uuid not null # research_journal_volume_id :uuid # university_id :uuid not null diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..49ba357dd1d4afc63ac0bae78dbcd1a882df9e9e --- /dev/null +++ b/app/views/active_storage/blobs/_blob.html.erb @@ -0,0 +1,14 @@ +<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>"> + <% if blob.representable? %> + <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> + <% end %> + + <figcaption class="attachment__caption"> + <% if caption = blob.try(:caption) %> + <%= caption %> + <% else %> + <span class="attachment__name"><%= blob.filename %></span> + <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span> + <% end %> + </figcaption> +</figure> diff --git a/config/initializers/action_text.rb b/config/initializers/action_text.rb new file mode 100644 index 0000000000000000000000000000000000000000..c0e43cbd0ea266efd23ebdcf6f782b92cfb443d1 --- /dev/null +++ b/config/initializers/action_text.rb @@ -0,0 +1,8 @@ +Rails.configuration.to_prepare do + ActionText::RichText.class_eval do + delegate :university, :university_id, to: :record + end + + ActionText::ContentHelper.allowed_tags += Rails.application.config.action_view.sanitized_allowed_tags + ActionText::ContentHelper.allowed_attributes += Rails.application.config.action_view.sanitized_allowed_attributes +end diff --git a/db/migrate/20211021093238_create_action_text_tables.action_text.rb b/db/migrate/20211021093238_create_action_text_tables.action_text.rb new file mode 100644 index 0000000000000000000000000000000000000000..bba5dc5076e10fd807b859cae4e18e88f73edf3f --- /dev/null +++ b/db/migrate/20211021093238_create_action_text_tables.action_text.rb @@ -0,0 +1,14 @@ +# This migration comes from action_text (originally 20180528164100) +class CreateActionTextTables < ActiveRecord::Migration[6.0] + def change + create_table :action_text_rich_texts, id: :uuid do |t| + t.string :name, null: false + t.text :body, size: :long + t.references :record, null: false, polymorphic: true, index: false, type: :uuid + + t.timestamps + + t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 33335b1a6728ebcbbcefa04562c7abd95e52c004..bb4be990c1026ec5e143e9ad9659da38c4033190 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,13 +10,23 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_10_20_090658) do +ActiveRecord::Schema.define(version: 2021_10_21_093238) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" - create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "action_text_rich_texts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.string "name", null: false + t.text "body" + t.string "record_type", null: false + t.uuid "record_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true + end + + create_table "active_storage_attachments", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false t.uuid "record_id", null: false @@ -26,7 +36,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end - create_table "active_storage_blobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "active_storage_blobs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "key", null: false t.string "filename", null: false t.string "content_type" @@ -40,13 +50,13 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["university_id"], name: "index_active_storage_blobs_on_university_id" end - create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "active_storage_variant_records", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "blob_id", null: false t.string "variation_digest", null: false t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end - create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.integer "number" t.text "name" t.text "description" @@ -54,7 +64,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.datetime "updated_at", precision: 6, null: false end - create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "criterion_id", null: false t.integer "number" t.text "name" @@ -68,7 +78,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["criterion_id"], name: "index_administration_qualiopi_indicators_on_criterion_id" end - create_table "communication_website_imported_media", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_website_imported_media", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "identifier" t.jsonb "data" t.text "file_url" @@ -83,7 +93,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["website_id"], name: "index_communication_website_imported_media_on_website_id" end - create_table "communication_website_imported_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_website_imported_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.uuid "website_id", null: false t.uuid "page_id", null: false @@ -106,7 +116,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["website_id"], name: "index_communication_website_imported_pages_on_website_id" end - create_table "communication_website_imported_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_website_imported_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.uuid "website_id", null: false t.uuid "post_id", null: false @@ -129,7 +139,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["website_id"], name: "index_communication_website_imported_posts_on_website_id" end - create_table "communication_website_imported_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_website_imported_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.uuid "website_id", null: false t.integer "status", default: 0 @@ -139,7 +149,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["website_id"], name: "index_communication_website_imported_websites_on_website_id" end - create_table "communication_website_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_website_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.uuid "communication_website_id", null: false t.string "title" @@ -160,7 +170,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["university_id"], name: "index_communication_website_pages_on_university_id" end - create_table "communication_website_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_website_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.uuid "communication_website_id", null: false t.string "title" @@ -175,7 +185,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["university_id"], name: "index_communication_website_posts_on_university_id" end - create_table "communication_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "communication_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.string "name" t.string "domain" @@ -204,7 +214,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["priority", "run_at"], name: "delayed_jobs_priority" end - create_table "education_programs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "education_programs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.string "name" t.integer "level" @@ -225,14 +235,14 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["university_id"], name: "index_education_programs_on_university_id" end - create_table "languages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "languages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "name" t.string "iso_code" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false end - create_table "research_journal_articles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "research_journal_articles", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "title" t.text "text" t.date "published_at" @@ -240,7 +250,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.uuid "research_journal_id", null: false t.uuid "research_journal_volume_id" t.datetime "created_at", precision: 6, null: false - t.date "updated_at", null: false + t.datetime "updated_at", precision: 6, null: false t.uuid "updated_by_id" t.text "abstract" t.text "references" @@ -258,7 +268,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["researcher_id"], name: "index_research_journal_articles_researchers_on_researcher_id" end - create_table "research_journal_volumes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "research_journal_volumes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.uuid "research_journal_id", null: false t.string "title" @@ -272,7 +282,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["university_id"], name: "index_research_journal_volumes_on_university_id" end - create_table "research_journals", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "research_journals", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.string "title" t.text "description" @@ -284,7 +294,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["university_id"], name: "index_research_journals_on_university_id" end - create_table "research_researchers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "research_researchers", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "first_name" t.string "last_name" t.text "biography" @@ -294,7 +304,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.index ["user_id"], name: "index_research_researchers_on_user_id" end - create_table "universities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "universities", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.string "name" t.string "identifier" t.string "address" @@ -309,7 +319,7 @@ ActiveRecord::Schema.define(version: 2021_10_20_090658) do t.string "sms_sender_name" end - create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + create_table "users", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| t.uuid "university_id", null: false t.string "first_name" t.string "last_name" diff --git a/test/fixtures/action_text/rich_texts.yml b/test/fixtures/action_text/rich_texts.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b371ea604af5fba7cacf8f712528c1e12949959 --- /dev/null +++ b/test/fixtures/action_text/rich_texts.yml @@ -0,0 +1,4 @@ +# one: +# record: name_of_fixture (ClassOfFixture) +# name: content +# body: <p>In a <i>million</i> stars!</p> diff --git a/vendor/assets/javascripts/actiontext.js b/vendor/assets/javascripts/actiontext.js new file mode 100644 index 0000000000000000000000000000000000000000..792a2c1fc310fd79f41117dcd4d3c5b888837776 --- /dev/null +++ b/vendor/assets/javascripts/actiontext.js @@ -0,0 +1,880 @@ +var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +var activestorage = {exports: {}}; + +(function (module, exports) { +(function(global, factory) { + factory(exports) ; +})(commonjsGlobal, (function(exports) { + 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) { + 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"); + 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) { + this.id = ++id; + this.file = file; + this.url = url; + this.delegate = delegate; + } + create(callback) { + FileChecksum.create(this.file, ((error, checksum) => { + if (error) { + callback(error); + return; + } + const blob = new BlobRecord(this.file, checksum, this.url); + 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 {target: target} = event; + if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) { + submitButtonsByForm.set(target.form, target); + } + } + 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.start = start; + Object.defineProperty(exports, "__esModule", { + value: true + }); +})); +}(activestorage, activestorage.exports)); + +class AttachmentUpload { + constructor(attachment, element) { + this.attachment = attachment; + this.element = element; + this.directUpload = new activestorage.exports.DirectUpload(attachment.file, this.directUploadUrl, this); + } + + start() { + this.directUpload.create(this.directUploadDidComplete.bind(this)); + } + + directUploadWillStoreFileWithXHR(xhr) { + xhr.upload.addEventListener("progress", event => { + const progress = event.loaded / event.total * 100; + this.attachment.setUploadProgress(progress); + }); + } + + directUploadDidComplete(error, attributes) { + if (error) { + throw new Error(`Direct upload failed: ${error}`) + } + + this.attachment.setAttributes({ + sgid: attributes.attachable_sgid, + url: this.createBlobUrl(attributes.signed_id, attributes.filename) + }); + } + + createBlobUrl(signedId, filename) { + return this.blobUrlTemplate + .replace(":signed_id", signedId) + .replace(":filename", encodeURIComponent(filename)) + } + + get directUploadUrl() { + return this.element.dataset.directUploadUrl + } + + get blobUrlTemplate() { + return this.element.dataset.blobUrlTemplate + } +} + +addEventListener("trix-attachment-add", event => { + const { attachment, target } = event; + + if (attachment.file) { + const upload = new AttachmentUpload(attachment, target); + upload.start(); + } +});