├── .eslintrc ├── .github ├── dependabot.yml └── workflows │ └── ruby.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .scss-lint.yml ├── Capfile ├── Gemfile ├── Gemfile.lock ├── ISSUE_TEMPLATE.md ├── Jenkinsfile ├── LICENSE ├── Procfile.dev ├── README.md ├── Rakefile ├── app ├── assets │ ├── builds │ │ └── .keep │ ├── fonts │ │ ├── sul-icons.eot │ │ ├── sul-icons.svg │ │ ├── sul-icons.ttf │ │ └── sul-icons.woff │ ├── images │ │ ├── .keep │ │ ├── by.png │ │ ├── by.svg │ │ ├── iiif-drag-icon.png │ │ ├── images │ │ │ ├── layers-2x.png │ │ │ ├── layers.png │ │ │ ├── marker-icon-2x.png │ │ │ ├── marker-icon.png │ │ │ └── marker-shadow.png │ │ ├── locked-media-poster.svg │ │ ├── pdm.png │ │ ├── pdm.svg │ │ ├── stanford_red_s.png │ │ ├── stanford_s.png │ │ ├── stanford_s.svg │ │ ├── sul_stacked_color.svg │ │ └── waveform-audio-poster.svg │ └── stylesheets │ │ ├── common.css │ │ ├── companion_window.css │ │ ├── content_list.css │ │ ├── document.css │ │ ├── file.css │ │ ├── geo.css │ │ ├── leaflet.css │ │ ├── locked_status.css │ │ ├── m3.css │ │ ├── media.css │ │ ├── model.css │ │ └── webarchive.css ├── components │ ├── companion_windows │ │ ├── authorization_messages_component.html.erb │ │ ├── authorization_messages_component.rb │ │ ├── content_list_component.html.erb │ │ ├── content_list_component.rb │ │ ├── content_not_available_component.html.erb │ │ ├── content_not_available_component.rb │ │ ├── control_button_component.rb │ │ ├── external_link_component.html.erb │ │ ├── external_link_component.rb │ │ ├── fullscreen_button_component.html.erb │ │ ├── fullscreen_button_component.rb │ │ ├── rights_component.html.erb │ │ ├── rights_component.rb │ │ ├── share_button_component.html.erb │ │ └── share_button_component.rb │ ├── companion_windows_component.html.erb │ ├── companion_windows_component.rb │ ├── dialog_component.html.erb │ ├── dialog_component.rb │ ├── document │ │ ├── content_list_item_component.html.erb │ │ └── content_list_item_component.rb │ ├── download │ │ ├── all_files_component.html.erb │ │ ├── all_files_component.rb │ │ ├── file_list_component.html.erb │ │ ├── file_list_component.rb │ │ ├── file_list_item_component.html.erb │ │ └── file_list_item_component.rb │ ├── download_access_component.html.erb │ ├── download_access_component.rb │ ├── embed │ │ └── file │ │ │ ├── auth_messages_component.html.erb │ │ │ ├── auth_messages_component.rb │ │ │ ├── dir_row_component.html.erb │ │ │ ├── dir_row_component.rb │ │ │ ├── file_row_component.html.erb │ │ │ └── file_row_component.rb │ ├── embed_this_form_component.html.erb │ ├── embed_this_form_component.rb │ ├── file_component.html.erb │ ├── file_component.rb │ ├── geo_component.html.erb │ ├── geo_component.rb │ ├── icons │ │ ├── audio_file_component.html.erb │ │ ├── audio_file_component.rb │ │ ├── description_component.html.erb │ │ ├── description_component.rb │ │ ├── download_component.html.erb │ │ ├── download_component.rb │ │ ├── file_present_component.html.erb │ │ ├── file_present_component.rb │ │ ├── folder_component.html.erb │ │ ├── folder_component.rb │ │ ├── folder_zip_component.html.erb │ │ ├── folder_zip_component.rb │ │ ├── image_component.html.erb │ │ ├── image_component.rb │ │ ├── insert_chart_component.html.erb │ │ ├── insert_chart_component.rb │ │ ├── insert_drive_file_component.html.erb │ │ ├── insert_drive_file_component.rb │ │ ├── lock_clock_component.html.erb │ │ ├── lock_clock_component.rb │ │ ├── lock_component.html.erb │ │ ├── lock_component.rb │ │ ├── lock_globe_component.html.erb │ │ ├── lock_globe_component.rb │ │ ├── picture_as_pdf_component.html.erb │ │ ├── picture_as_pdf_component.rb │ │ ├── stanford_only_component.html.erb │ │ ├── stanford_only_component.rb │ │ ├── terminal_component.html.erb │ │ ├── terminal_component.rb │ │ ├── video_file_component.html.erb │ │ ├── video_file_component.rb │ │ ├── web_archive_component.html.erb │ │ └── web_archive_component.rb │ ├── iframe_component.rb │ ├── locked_status_component.rb │ ├── login_component.html.erb │ ├── login_component.rb │ ├── m3_component.html.erb │ ├── m3_component.rb │ ├── media │ │ ├── prev_next_component.html.erb │ │ ├── prev_next_component.rb │ │ ├── preview_image_component.rb │ │ ├── tag_component.rb │ │ └── wrapper_component.rb │ ├── media_component.html.erb │ ├── media_component.rb │ ├── model_component.html.erb │ ├── model_component.rb │ ├── pdf_component.html.erb │ ├── pdf_component.rb │ ├── restricted_message_component.html.erb │ ├── restricted_message_component.rb │ ├── tabpanel_component.html.erb │ ├── tabpanel_component.rb │ ├── view_access_component.html.erb │ ├── view_access_component.rb │ ├── web_archive_component.html.erb │ └── web_archive_component.rb ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── embed_controller.rb │ └── pages_controller.rb ├── javascript │ ├── controllers │ │ ├── application.js │ │ ├── clipboard_controller.js │ │ ├── companion_window_controller.js │ │ ├── content_list_controller.js │ │ ├── cue_controller.js │ │ ├── embed_this_controller.js │ │ ├── file_auth_controller.js │ │ ├── fullscreen_controller.js │ │ ├── geo_controller.js │ │ ├── iiif_auth_restriction_controller.js │ │ ├── iiif_manifest_loader_controller.js │ │ ├── iiif_metadata_controller.js │ │ ├── image_controller.js │ │ ├── index.js │ │ ├── locked_media_poster_controller.js │ │ ├── locked_poster_controller.js │ │ ├── media_player_controller.js │ │ ├── media_tag_controller.js │ │ ├── media_wrapper_controller.js │ │ ├── meta_check_controller.js │ │ ├── model_controller.js │ │ ├── osd_controller.js │ │ ├── pdf_controller.js │ │ ├── prev_next_controller.js │ │ ├── tab_controller.js │ │ ├── tooltip_controller.js │ │ └── transcript_controller.js │ ├── document.js │ ├── file.js │ ├── file_controllers │ │ ├── application.js │ │ ├── download_all_controller.js │ │ ├── index.js │ │ └── tree_controller.js │ ├── geo.js │ ├── geo │ │ └── leaflet_opacity.js │ ├── media.js │ ├── model.js │ ├── packs │ │ └── m3.js │ ├── src │ │ ├── components │ │ │ ├── CdlAuthenticationControl.jsx │ │ │ ├── CdlCopyright.jsx │ │ │ ├── CdlCountdown.jsx │ │ │ ├── CdlLoginWindowSizing.jsx │ │ │ ├── CdlLogout.jsx │ │ │ ├── DueDate.jsx │ │ │ └── embedMode.jsx │ │ ├── modules │ │ │ ├── m3_viewer.js │ │ │ ├── metrics.js │ │ │ └── thumbnail.js │ │ └── plugins │ │ │ ├── analyticsPlugin.js │ │ │ ├── cdlAuthPlugin.js │ │ │ ├── embedModePlugin.js │ │ │ ├── miradorZoomBugPlugin.jsx │ │ │ ├── shareMenuPlugin.jsx │ │ │ └── xywhPlugin.js │ └── webarchive.js ├── models │ ├── .keep │ ├── concerns │ │ └── .keep │ └── embed │ │ ├── envelope.rb │ │ ├── hierarchical_contents.rb │ │ ├── purl.rb │ │ ├── purl │ │ ├── file_json_deserializer.rb │ │ ├── media_file.rb │ │ ├── resource.rb │ │ ├── resource_dir.rb │ │ ├── resource_file.rb │ │ └── resource_json_deserializer.rb │ │ ├── purl_json_loader.rb │ │ ├── response.rb │ │ └── was_time_map.rb ├── viewers │ └── embed │ │ ├── viewer │ │ ├── authorization.rb │ │ ├── common_viewer.rb │ │ ├── document_viewer.rb │ │ ├── file.rb │ │ ├── geo.rb │ │ ├── m3_viewer.rb │ │ ├── media.rb │ │ ├── model_viewer.rb │ │ └── was_seed.rb │ │ └── viewer_factory.rb └── views │ ├── embed │ ├── _analytics.html.erb │ ├── iframe.html.erb │ └── iiif.html.erb │ ├── layouts │ └── preview │ │ ├── file.html.erb │ │ ├── geo.html.erb │ │ ├── media.html.erb │ │ ├── model.html.erb │ │ └── pdf.html.erb │ └── pages │ ├── home.html.erb │ └── sandbox.html.erb ├── babel.config.js ├── bin ├── brakeman ├── bundle ├── dev ├── importmap ├── rails ├── rake ├── rdbg ├── rubocop ├── setup ├── shakapacker ├── shakapacker-dev-server ├── thrust ├── update └── yarn ├── compose.yaml ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── deploy.rb ├── deploy │ ├── prod.rb │ ├── stage.rb │ └── uat.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── importmap.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── config.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── high_voltage.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── okcomputer.rb │ ├── permissions_policy.rb │ └── wrap_parameters.rb ├── licenses.yml ├── locales │ ├── en.yml │ └── okcomputer_en.yml ├── puma.rb ├── routes.rb ├── settings.yml ├── settings │ ├── development.yml │ ├── production.yml │ └── test.yml ├── shakapacker.yml ├── spring.rb └── webpack │ ├── development.js │ ├── production.js │ └── webpack.config.js ├── lib ├── assets │ └── .keep ├── bcp47.rb ├── bcp47 │ ├── parser.rb │ ├── record.rb │ └── registry.rb ├── constants.rb ├── embed.rb ├── embed │ ├── mimetypes.rb │ ├── pretty_filesize.rb │ ├── request.rb │ ├── response.rb │ └── stacks_image.rb ├── size_converter.rb └── tasks │ ├── .keep │ └── precompile.rake ├── log └── .keep ├── package.json ├── postcss.config.js ├── public ├── 400.html ├── 404.html ├── 406-unsupported-browser.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── spec ├── components │ ├── companion_windows │ │ ├── content_list_component_spec.rb │ │ ├── control_button_component_spec.rb │ │ ├── fullscreen_button_component_spec.rb │ │ ├── rights_component_spec.rb │ │ └── share_button_component_spec.rb │ ├── companion_windows_component_spec.rb │ ├── dialog_component_spec.rb │ ├── download │ │ ├── all_files_component_spec.rb │ │ └── file_list_component_spec.rb │ ├── embed │ │ └── file │ │ │ └── auth_messages_component_spec.rb │ ├── embed_this_form_component_spec.rb │ ├── file_component_spec.rb │ ├── geo_component_spec.rb │ ├── icons │ │ └── stanford_only_component_spec.rb │ ├── iframe_component_spec.rb │ ├── m3_component_spec.rb │ ├── media │ │ ├── preview_image_component_spec.rb │ │ ├── tag_component_spec.rb │ │ └── wrapper_component_spec.rb │ ├── media_component_spec.rb │ ├── model_component_spec.rb │ ├── pdf_component_spec.rb │ ├── restricted_message_component_spec.rb │ └── web_archive_component_spec.rb ├── factories │ ├── files.rb │ ├── purls.rb │ └── resources.rb ├── features │ ├── 3d_viewer_spec.rb │ ├── document_viewer_spec.rb │ ├── embed_this_panel_spec.rb │ ├── file_hierarchy_spec.rb │ ├── file_search_spec.rb │ ├── file_viewer_spec.rb │ ├── geo_viewer_spec.rb │ ├── iiif_embed_spec.rb │ ├── media_viewer_spec.rb │ ├── metrics_spec.rb │ ├── sandbox_spec.rb │ ├── status_spec.rb │ └── was_seed_viewer_spec.rb ├── fixtures │ ├── purl_fixtures.rb │ └── was_time_map_fixtures.rb ├── lib │ ├── bcp47 │ │ ├── parser_spec.rb │ │ ├── record_spec.rb │ │ └── registry_spec.rb │ └── embed │ │ ├── mimetypes_spec.rb │ │ ├── pretty_filesize_spec.rb │ │ ├── request_spec.rb │ │ ├── response_spec.rb │ │ ├── stacks_image_spec.rb │ │ ├── viewer │ │ ├── common_viewer_spec.rb │ │ ├── file_spec.rb │ │ ├── geo_spec.rb │ │ ├── m3_viewer_spec.rb │ │ ├── media_spec.rb │ │ ├── model_viewer_spec.rb │ │ └── was_seed_spec.rb │ │ └── viewer_factory_spec.rb ├── models │ └── embed │ │ ├── envelope_spec.rb │ │ ├── hierarchical_contents_spec.rb │ │ ├── purl │ │ ├── media_file_spec.rb │ │ ├── resource_file_spec.rb │ │ └── resource_spec.rb │ │ ├── purl_json_loader_spec.rb │ │ ├── purl_spec.rb │ │ └── was_time_map_spec.rb ├── rails_helper.rb ├── requests │ └── embed_spec.rb ├── spec_helper.rb └── support │ ├── metrics_helper.rb │ └── stub_apps │ └── stub_metrics_api.rb ├── test └── components │ └── previews │ └── embed │ ├── companion_window_component_preview.rb │ ├── file_component_preview.rb │ ├── geo_component_preview.rb │ ├── legacy │ ├── geo_component_preview.rb │ └── model_component_preview.rb │ ├── media_component_preview.rb │ ├── model_component_preview.rb │ └── pdf_component_preview.rb ├── vendor ├── data │ └── language-subtag-registry └── javascript │ ├── .keep │ ├── Leaflet.Control.Custom.js │ └── leaflet.js └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest/globals": true 4 | }, 5 | "extends": ["airbnb","react-app"], 6 | "globals": { 7 | "page": true, 8 | "document": true 9 | }, 10 | "parser": "babel-eslint", 11 | "plugins": ["jest"], 12 | "rules": { 13 | "import/prefer-default-export": "off", 14 | "no-console": "off", 15 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 16 | "require-jsdoc": ["error", { 17 | "require": { 18 | "FunctionDeclaration": true, 19 | "MethodDefinition": true, 20 | "ClassDeclaration": true, 21 | "ArrowFunctionExpression": true, 22 | "FunctionExpression": true 23 | } 24 | }], 25 | "no-underscore-dangle": "off", 26 | "react/prefer-stateless-function": "off", 27 | "sort-keys": ["error", "asc", { 28 | "caseSensitive": false, 29 | "natural": false 30 | }], 31 | "react/jsx-props-no-spreading": "off", 32 | "arrow-parens": "off", 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | ruby: ["3.4"] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Ruby 18 | uses: ruby/setup-ruby@v1 19 | with: 20 | ruby-version: ${{ matrix.ruby }} 21 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: 18 25 | - run: bin/yarn install 26 | - name: Run tests 27 | run: bin/rake 28 | env: 29 | RAILS_ENV: test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore capistrano config. 11 | /.capistrano 12 | 13 | # Ignore ruby version config. 14 | /.ruby-version 15 | 16 | # Ignore the default SQLite database. 17 | /db/*.sqlite3 18 | /db/*.sqlite3-journal 19 | 20 | # Ignore all logfiles and tempfiles. 21 | /log/*.log 22 | /tmp 23 | 24 | config/settings.local.yml 25 | config/settings/*.local.yml 26 | config/environments/*.local.yml 27 | 28 | /node_modules 29 | coverage 30 | 31 | .pry_history 32 | 33 | /public/assets 34 | /public/packs 35 | /public/packs-test 36 | /public/file 37 | /node_modules 38 | /yarn-error.log 39 | yarn-debug.log* 40 | .yarn-integrity 41 | 42 | /app/assets/builds/* 43 | !/app/assets/builds/.keep 44 | 45 | .rspec_status 46 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /Capfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load DSL and Setup Up Stages 4 | require 'capistrano/setup' 5 | 6 | # Includes default deployment tasks 7 | require 'capistrano/deploy' 8 | require 'capistrano/scm/git' 9 | install_plugin Capistrano::SCM::Git 10 | 11 | # Includes tasks from other gems included in your Gemfile 12 | # 13 | # For documentation on these, see for example: 14 | # 15 | # https://github.com/capistrano/rvm 16 | # https://github.com/capistrano/rbenv 17 | # https://github.com/capistrano/chruby 18 | # https://github.com/capistrano/bundler 19 | # https://github.com/capistrano/rails 20 | # 21 | # require 'capistrano/rvm' 22 | # require 'capistrano/rbenv' 23 | # require 'capistrano/chruby' 24 | # require 'capistrano/bundler' 25 | # require 'capistrano/rails/assets' 26 | # require 'capistrano/rails/migrations' 27 | 28 | require 'capistrano/bundler' 29 | require 'capistrano/rails/assets' 30 | require 'capistrano/honeybadger' 31 | require 'capistrano/passenger' 32 | require 'capistrano/shared_configs' 33 | require 'dlss/capistrano' 34 | 35 | # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. 36 | Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } 37 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | gem 'rails', '~> 8.0.0' 7 | 8 | gem 'propshaft' 9 | 10 | # Use Puma as the app server 11 | gem 'puma', '~> 6.0' 12 | 13 | # Reduces boot times through caching; required in config/boot.rb 14 | gem 'bootsnap', '>= 1.1.0', require: false 15 | gem 'faraday', '~> 2' 16 | gem 'faraday-follow_redirects' 17 | 18 | gem 'config' 19 | gem 'dor-rights-auth' 20 | gem 'nokogiri', '>= 1.7.1' 21 | 22 | group :development, :test do 23 | gem 'capybara' 24 | gem 'debug', platforms: %i[mri] 25 | gem 'druid-tools' 26 | gem 'factory_bot_rails', '~> 6.4' 27 | gem 'high_voltage' 28 | gem 'rspec' 29 | gem 'rspec-rails' 30 | 31 | gem 'selenium-webdriver', '~> 4.2' 32 | 33 | gem 'webmock', '~> 3.19' 34 | 35 | # Linting/Styleguide Enforcement 36 | gem 'rubocop', '~> 1.53' 37 | gem 'rubocop-capybara', require: false 38 | gem 'rubocop-factory_bot', require: false 39 | gem 'rubocop-performance', require: false 40 | gem 'rubocop-rails', require: false 41 | gem 'rubocop-rspec', require: false 42 | gem 'rubocop-rspec_rails', require: false 43 | end 44 | 45 | group :deployment do 46 | gem 'capistrano', '~> 3.0' 47 | gem 'capistrano-bundler' 48 | gem 'capistrano-passenger' 49 | gem 'capistrano-rails' 50 | gem 'capistrano-shared_configs' 51 | gem 'dlss-capistrano' 52 | end 53 | 54 | # Use honeybadger for exception handling 55 | gem 'honeybadger' 56 | 57 | gem 'newrelic_rpm', group: :production 58 | 59 | # Use okcomputer to monitor the application 60 | gem 'okcomputer' 61 | 62 | gem 'shakapacker', '~> 7.0' 63 | 64 | gem 'view_component', '~> 3.10' 65 | 66 | gem 'importmap-rails', '~> 2.0' 67 | 68 | gem 'stimulus-rails', '~> 1.3' 69 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected behavior 2 | 3 | ### Actual behavior 4 | 5 | ### Steps to reproduce the behavior 6 | 7 | ### Browser / Environment 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Board of Trustees of the Leland Stanford Junior University. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | web: unset PORT && env RUBY_DEBUG_OPEN=true bin/rails server 2 | js: bin/shakapacker-dev-server 3 | stacks: docker compose up --abort-on-container-exit --build 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | 8 | begin 9 | require 'rubocop/rake_task' 10 | RuboCop::RakeTask.new 11 | rescue LoadError 12 | # should only be here when gem group development and test aren't installed 13 | end 14 | 15 | # remove default rspec task 16 | task(:default).clear 17 | 18 | task default: ['test:prepare', :spec, :rubocop] 19 | 20 | task asset_paths: [:environment] do 21 | puts Rails.application.config.assets.paths 22 | end 23 | 24 | task :update_language_tags do 25 | File.open(Settings.language_subtag_registry.path, 'w') do |file| 26 | file.write(Faraday.get(Settings.language_subtag_registry.url).body) 27 | end 28 | end 29 | 30 | task :stackify, [:bare_druid] => :environment do |_task, args| 31 | exit(1) unless Rails.env.development? 32 | 33 | bare_druid = args[:bare_druid] 34 | druid_path = DruidTools::StacksDruid.new(bare_druid, Settings.stacks_storage_root).path 35 | `mkdir -p #{druid_path}` 36 | Embed::Purl.find(bare_druid).downloadable_files.each do |resource_file| 37 | puts "Downloading #{resource_file.filename} to #{druid_path}/#{resource_file.filename}" 38 | `curl https://stacks.stanford.edu/file/#{bare_druid}/#{resource_file.filename} -o #{druid_path}/#{resource_file.filename}` 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/assets/builds/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/builds/.keep -------------------------------------------------------------------------------- /app/assets/fonts/sul-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/fonts/sul-icons.eot -------------------------------------------------------------------------------- /app/assets/fonts/sul-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/fonts/sul-icons.ttf -------------------------------------------------------------------------------- /app/assets/fonts/sul-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/fonts/sul-icons.woff -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/by.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/by.png -------------------------------------------------------------------------------- /app/assets/images/iiif-drag-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/iiif-drag-icon.png -------------------------------------------------------------------------------- /app/assets/images/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/images/layers-2x.png -------------------------------------------------------------------------------- /app/assets/images/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/images/layers.png -------------------------------------------------------------------------------- /app/assets/images/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/images/marker-icon-2x.png -------------------------------------------------------------------------------- /app/assets/images/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/images/marker-icon.png -------------------------------------------------------------------------------- /app/assets/images/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/images/marker-shadow.png -------------------------------------------------------------------------------- /app/assets/images/pdm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/pdm.png -------------------------------------------------------------------------------- /app/assets/images/stanford_red_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/stanford_red_s.png -------------------------------------------------------------------------------- /app/assets/images/stanford_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sul-dlss/sul-embed/1ab7cc56b68132ae08de850597d241971565691f/app/assets/images/stanford_s.png -------------------------------------------------------------------------------- /app/assets/images/stanford_s.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/stylesheets/common.css: -------------------------------------------------------------------------------- 1 | /* For styles shared between the legacy and modern viewers */ 2 | :root { 3 | --link-color: #006cb8; 4 | --stanford-40-black: #ababa9; 5 | } 6 | /* From Bootstrap 5.3 */ 7 | .visually-hidden-focusable:not(:focus):not(:focus-within):not(caption), 8 | .visually-hidden:not(caption) { 9 | position: absolute !important; 10 | } 11 | 12 | .visually-hidden, 13 | .visually-hidden-focusable:not(:focus):not(:focus-within) { 14 | width: 1px !important; 15 | height: 1px !important; 16 | padding: 0 !important; 17 | margin: -1px !important; 18 | overflow: hidden !important; 19 | clip: rect(0, 0, 0, 0) !important; 20 | white-space: nowrap !important; 21 | border: 0 !important; 22 | } 23 | 24 | a, 25 | a:hover, 26 | a:focus { 27 | color: var(--link-color); 28 | } 29 | 30 | /* TODO: Remove this class and replace with Icons::StanfordOnlyComponent when we remove legacy file viewer */ 31 | .sul-embed-stanford-only-text { 32 | --vertical-align-position: top; 33 | background: 34 | url("stanford_s.svg") no-repeat left, 35 | none; 36 | display: inline-block; 37 | height: 15px; 38 | width: 15px; 39 | vertical-align: var(--vertical-align-position); 40 | } 41 | 42 | .left-drawer .sul-embed-stanford-only-text { 43 | --vertical-align-position: sub; 44 | } 45 | 46 | .sul-embed-location-restricted-text, .stanford-digital-red { 47 | color: var(--stanford-digital-red); 48 | } 49 | 50 | .su-underline { 51 | text-decoration: underline dotted var(--stanford-40-black) 1px; 52 | text-underline-position: under; 53 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/document.css: -------------------------------------------------------------------------------- 1 | @import url("companion_window.css"); 2 | @import url("content_list.css"); 3 | @import url("common.css"); 4 | @import url("locked_status.css"); 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/locked_status.css: -------------------------------------------------------------------------------- 1 | .locked-status { 2 | display: block; 3 | overflow: hidden; 4 | height: 100%; 5 | text-align: center; 6 | 7 | img { 8 | height: 100%; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/m3.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --gray-80: #ccc; 3 | } 4 | 5 | .mosaic-root { 6 | bottom: 0 !important; 7 | left: 0 !important; 8 | right: 0 !important; 9 | top: 0 !important; 10 | } 11 | 12 | .mosaic-tile { 13 | margin: 0 !important; 14 | } 15 | 16 | .mosaic-split.-row, 17 | .mosaic-split.-column { 18 | background-color: #aaa; 19 | } 20 | 21 | .sul-embed-container { 22 | border: 0; 23 | border-left: 1px solid var(--gray-80); 24 | box-shadow: 25 | 0 1px 3px 0 rgba(0, 0, 0, 0.2), 26 | 0 1px 1px 0 rgba(0, 0, 0, 0.2), 27 | 0 2px 1px -1px rgba(0, 0, 0, 0.2); 28 | height: calc(100vh - 3px); /* Give a little space for the box shadow */ 29 | margin: 0 1px 3px 0; 30 | position: relative; 31 | 32 | /* For single-window M3 embeds, this will be overriden by Mirador 3's 33 | selected window top-bar styling, but if there are mulitple 34 | windows, it will give the unselected windows a visible border. */ 35 | .mirador-window-top-bar { 36 | border-top-color: var(--gray-80); 37 | } 38 | } 39 | 40 | .sul-embed-container { 41 | .mirador-window-sidebar-annotation-panel { 42 | .MuiMenuItem-root { 43 | white-space: normal; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/assets/stylesheets/model.css: -------------------------------------------------------------------------------- 1 | @import url("companion_window.css"); 2 | @import url("common.css"); 3 | @import url("locked_status.css"); 4 | 5 | /* stops scroll for lock icon which is caused by sul-embed-3d position: relative */ 6 | #main-display { 7 | overflow: hidden; 8 | } 9 | 10 | .sul-embed-3d { 11 | display: inline-block; 12 | position: relative; 13 | height: 100%; 14 | width: 100%; 15 | 16 | .buttons { 17 | left: 10px; 18 | position: absolute; 19 | top: 10px; 20 | z-index: 1; 21 | } 22 | 23 | .zoom-in, 24 | .zoom-out { 25 | background: transparent; 26 | border: none; 27 | color: white; 28 | cursor: pointer; 29 | font-size: 2em; 30 | padding: 5px 13px; 31 | } 32 | 33 | .model-viewer-container, model-viewer { 34 | height: 100%; 35 | width: 100%; 36 | } 37 | 38 | model-viewer { 39 | background-color: black; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/assets/stylesheets/webarchive.css: -------------------------------------------------------------------------------- 1 | @import url("companion_window.css"); 2 | @import url("common.css"); 3 | 4 | .sul-embed-was-seed-container { 5 | overflow-y: scroll; 6 | } 7 | 8 | .sul-embed-was-seed-content { 9 | padding: 10px; 10 | } 11 | 12 | .sul-embed-was-seed-row { 13 | display: flex; 14 | flex-direction: row; 15 | flex-wrap: wrap; 16 | } 17 | 18 | .sul-embed-was-seed-column { 19 | display: flex; 20 | flex-direction: column; 21 | flex: 1; 22 | padding: 0 12px 12px 12px; 23 | } 24 | 25 | .sul-embed-was-seed-preview { 26 | img { 27 | max-height: 200px; 28 | } 29 | } 30 | 31 | .sul-embed-was-seed-captures { 32 | padding-top: 6px; 33 | h1 { 34 | font-size: 1.5rem; 35 | font-weight: 400; 36 | } 37 | } 38 | 39 | .sul-embed-was-seed-info { 40 | flex-basis: 200px; 41 | max-width: 226px; 42 | padding-top: 12px; 43 | position: relative; 44 | 45 | a { 46 | border: 1px solid var(--border-color); 47 | text-align: center; 48 | } 49 | } 50 | 51 | .sul-embed-was-seed-list { 52 | display: flex; 53 | flex-wrap: wrap; 54 | list-style-type: none; 55 | padding-top: 12px; 56 | } 57 | 58 | .sul-embed-was-seed-list-item { 59 | border: 1px solid var(--border-color); 60 | margin: 0 15px 15px 0; 61 | min-width: 225px; 62 | padding: 10px; 63 | position: relative; 64 | text-align: center; 65 | color: var(--button-color); 66 | 67 | a { 68 | vertical-align: sub; 69 | } 70 | 71 | svg { 72 | vertical-align: middle; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/components/companion_windows/authorization_messages_component.html.erb: -------------------------------------------------------------------------------- 1 |
<%= I18n.t('restrictions.not_accessible') %>
6 |Bulk download not available. Please <%= mail_to "sdr-contact@lists.stanford.edu", "contact us", subject: "Download SDR object #{purl_object.druid}" %> if you need help downloading. Total files in this item: <%= downloadable_files.length %>, total size: <%= pretty_filesize %>. 8 |
9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/components/download/all_files_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Download 4 | class AllFilesComponent < ViewComponent::Base 5 | # @param [#purl_object] viewer 6 | def initialize(viewer:) 7 | @viewer = viewer 8 | end 9 | 10 | attr_reader :viewer 11 | 12 | delegate :purl_object, :download_url, :any_stanford_only_files?, to: :viewer 13 | delegate :downloadable_files, to: :purl_object 14 | 15 | def render? 16 | !viewer.is_a?(Embed::Viewer::Media) 17 | end 18 | 19 | # Returns true or false whether the viewer should display the Download All 20 | # link. The limits were determined in testing and may need to be adjusted 21 | # based on experience with download performance and any changes in the 22 | # Stacks API. It returns false when there is just one file because the 23 | # file download link will suffice for that. 24 | def display_download_all? 25 | purl_object.size < 10_737_418_240 && downloadable_files.length < 3000 26 | end 27 | 28 | def pretty_filesize 29 | viewer.pretty_filesize(purl_object.size) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/components/download/file_list_component.html.erb: -------------------------------------------------------------------------------- 1 | <% if downloadable_files.present? %> 2 |No downloads available
25 | <% end %> 26 | -------------------------------------------------------------------------------- /app/components/download/file_list_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Download 4 | class FileListComponent < ViewComponent::Base 5 | # @param [#purl_object] viewer 6 | def initialize(viewer:) 7 | @viewer = viewer 8 | end 9 | 10 | attr_reader :viewer 11 | 12 | delegate :purl_object, to: :viewer 13 | delegate :downloadable_files, to: :purl_object 14 | 15 | def grouped_downloadable_files 16 | purl_object.contents.map do |item| 17 | item.dup.tap { |obj| obj.files = obj.files.select(&:downloadable?) } 18 | end 19 | end 20 | 21 | # Determine if we need to group files. 22 | # For example, if a media file has a caption or transcript 23 | # we will want to group the caption with the media file. 24 | def grouped_files? 25 | viewer.is_a?(Embed::Viewer::DocumentViewer) || 26 | viewer.is_a?(Embed::Viewer::Geo) || 27 | viewer.is_a?(Embed::Viewer::ModelViewer) || 28 | downloadable_files.any? { |file| file.caption? || file.transcript? } 29 | end 30 | 31 | # File viewer does not show single file download links because it has these links in the main panel 32 | def single_file_download? 33 | !viewer.is_a?(Embed::Viewer::File) 34 | end 35 | 36 | def prefer_filename 37 | viewer.is_a?(Embed::Viewer::Geo) || viewer.is_a?(Embed::Viewer::ModelViewer) 38 | end 39 | 40 | def pretty_filesize 41 | viewer.pretty_filesize(purl_object.size) 42 | end 43 | 44 | def version 45 | purl_object.version_id 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /app/components/download/file_list_item_component.html.erb: -------------------------------------------------------------------------------- 1 |4 | <%= message[:message] %> 5 |
6 |<%= label %>
11 | <% end %> 12 |<%= I18n.t('restrictions.stanford_only') %>
4 | 8 |<%= I18n.t('restrictions.logged_in') %>
14 | 19 |