diff --git a/.gitignore b/.gitignore index 48fb168f..3d413911 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,58 @@ /.bundle # Ignore all logfiles and tempfiles. -/log/* -/tmp/* +*/log/* +*/tmp/* !/log/.keep !/tmp/.keep # Ignore Byebug command history file. .byebug_history + +# Ignore Mac files +*.DS_Store + +# Ignore Visual Studio files +.vscode + +# Ignore caches +*.sass-cache + +/public/swagger/ + +*.idea + +# React app stuff + +# dependencies +*/node_modules + +# testing +*/coverage + +# production +*/build + +# documentation +styleguide + +# misc +*.DS_Store + +*.env.local +*.env.development.local +*.env.test.local +*.env.production.local + +*npm-debug.log* +*yarn-debug.log* +*yarn-error.log* + +coverage +jest_0 +test.log +*.xml +!/viscoll-api/spec/fixtures/*.xml + +# DIY images +viscoll-api/uploads/* diff --git a/.ruby-gemset b/.ruby-gemset deleted file mode 100644 index 7d2389a7..00000000 --- a/.ruby-gemset +++ /dev/null @@ -1 +0,0 @@ -viscollobns diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 262714f1..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-2.4.0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..f90f583b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:16.04 + +# Install tools & libs to compile everything +RUN apt-get update && \ + apt-get install -y curl tzdata build-essential libssl-dev libreadline-dev wget && \ + apt-get clean + +# Install nodejs +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN apt-get install -y nodejs && apt-get clean + +# Install ruby-build +RUN apt-get install -y git-core && apt-get clean +RUN git clone https://github.com/sstephenson/ruby-build.git && cd ruby-build && ./install.sh + +# Install ruby 2.4.1 +ENV CONFIGURE_OPTS --disable-install-rdoc +RUN ruby-build 2.6.4 /usr/local +RUN gem install bundler +RUN gem install tzinfo-data + +# Clean up downloaded packages +RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 7dbe4e28..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,269 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - actioncable (5.0.2) - actionpack (= 5.0.2) - nio4r (>= 1.2, < 3.0) - websocket-driver (~> 0.6.1) - actionmailer (5.0.2) - actionpack (= 5.0.2) - actionview (= 5.0.2) - activejob (= 5.0.2) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (5.0.2) - actionview (= 5.0.2) - activesupport (= 5.0.2) - rack (~> 2.0) - rack-test (~> 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.2) - activesupport (= 5.0.2) - builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.0.2) - activesupport (= 5.0.2) - globalid (>= 0.3.6) - activemodel (5.0.2) - activesupport (= 5.0.2) - activerecord (5.0.2) - activemodel (= 5.0.2) - activesupport (= 5.0.2) - arel (~> 7.0) - activesupport (5.0.2) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.5.0) - public_suffix (~> 2.0, >= 2.0.2) - arel (7.1.4) - autoprefixer-rails (6.7.6) - execjs - bcrypt (3.1.11) - bootstrap-sass (3.3.7) - autoprefixer-rails (>= 5.2.1) - sass (>= 3.3.4) - bson (4.2.1) - builder (3.2.3) - byebug (9.0.6) - capybara (2.12.1) - addressable - mime-types (>= 1.16) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (~> 2.0) - concurrent-ruby (1.0.5) - cucumber (2.4.0) - builder (>= 2.1.2) - cucumber-core (~> 1.5.0) - cucumber-wire (~> 0.0.1) - diff-lcs (>= 1.1.3) - gherkin (~> 4.0) - multi_json (>= 1.7.5, < 2.0) - multi_test (>= 0.1.2) - cucumber-core (1.5.0) - gherkin (~> 4.0) - cucumber-rails (1.4.5) - capybara (>= 1.1.2, < 3) - cucumber (>= 1.3.8, < 4) - mime-types (>= 1.16, < 4) - nokogiri (~> 1.5) - railties (>= 3, < 5.1) - cucumber-wire (0.0.1) - database_cleaner (1.5.3) - debug_inspector (0.0.2) - devise (4.2.0) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.1) - responders - warden (~> 1.2.3) - diff-lcs (1.3) - email_spec (2.1.0) - htmlentities (~> 4.3.3) - launchy (~> 2.1) - mail (~> 2.6.3) - erubis (2.7.0) - execjs (2.7.0) - factory_girl (4.8.0) - activesupport (>= 3.0.0) - factory_girl_rails (4.8.0) - factory_girl (~> 4.8.0) - railties (>= 3.0.0) - faker (1.7.3) - i18n (~> 0.5) - ffi (1.9.18) - gherkin (4.0.0) - globalid (0.3.7) - activesupport (>= 4.1.0) - htmlentities (4.3.4) - i18n (0.8.1) - jbuilder (2.6.3) - activesupport (>= 3.0.0, < 5.2) - multi_json (~> 1.2) - jquery-rails (4.2.2) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) - launchy (2.4.3) - addressable (~> 2.3) - listen (3.0.8) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - loofah (2.0.3) - nokogiri (>= 1.5.9) - mail (2.6.4) - mime-types (>= 1.16, < 4) - method_source (0.8.2) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mini_portile2 (2.1.0) - minitest (5.10.1) - mongo (2.4.1) - bson (>= 4.2.1, < 5.0.0) - mongoid (6.1.0) - activemodel (~> 5.0) - mongo (>= 2.4.1, < 3.0.0) - mongoid-rspec (1.10.0) - mongoid (>= 3.0.1) - rake - rspec (>= 2.14) - multi_json (1.12.1) - multi_test (0.1.2) - nio4r (2.0.0) - nokogiri (1.7.0.1) - mini_portile2 (~> 2.1.0) - orm_adapter (0.5.0) - public_suffix (2.0.5) - puma (3.7.1) - rack (2.0.1) - rack-test (0.6.3) - rack (>= 1.0) - rails (5.0.2) - actioncable (= 5.0.2) - actionmailer (= 5.0.2) - actionpack (= 5.0.2) - actionview (= 5.0.2) - activejob (= 5.0.2) - activemodel (= 5.0.2) - activerecord (= 5.0.2) - activesupport (= 5.0.2) - bundler (>= 1.3.0, < 2.0) - railties (= 5.0.2) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.2) - activesupport (>= 4.2.0, < 6.0) - nokogiri (~> 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.0.2) - actionpack (= 5.0.2) - activesupport (= 5.0.2) - method_source - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) - rake (12.0.0) - rb-fsevent (0.9.8) - rb-inotify (0.9.8) - ffi (>= 0.5.0) - responders (2.3.0) - railties (>= 4.2.0, < 5.1) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-mocks (3.5.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-rails (3.5.2) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) - sass (3.4.23) - sass-rails (5.0.6) - railties (>= 4.0.0, < 6) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) - spring (2.0.1) - activesupport (>= 4.2) - spring-watcher-listen (2.0.1) - listen (>= 2.7, < 4.0) - spring (>= 1.2, < 3.0) - sprockets (3.7.1) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.0) - actionpack (>= 4.0) - activesupport (>= 4.0) - sprockets (>= 3.0.0) - thor (0.19.4) - thread_safe (0.3.6) - tilt (2.0.6) - tzinfo (1.2.2) - thread_safe (~> 0.1) - uglifier (3.1.5) - execjs (>= 0.3.0, < 3) - warden (1.2.7) - rack (>= 1.0) - web-console (3.4.0) - actionview (>= 5.0) - activemodel (>= 5.0) - debug_inspector - railties (>= 5.0) - websocket-driver (0.6.5) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) - xpath (2.0.0) - nokogiri (~> 1.3) - -PLATFORMS - ruby - -DEPENDENCIES - bcrypt (~> 3.1.7) - bootstrap-sass - byebug - capybara - cucumber-rails - database_cleaner - devise - email_spec - factory_girl_rails - faker - jbuilder (~> 2.5) - jquery-rails - launchy - listen (~> 3.0.5) - mongoid - mongoid-rspec - puma (~> 3.0) - rails (~> 5.0.2) - rspec - rspec-rails - sass-rails (~> 5.0) - spring - spring-watcher-listen (~> 2.0.0) - tzinfo-data - uglifier (>= 1.3.0) - web-console (>= 3.3.0) - -BUNDLED WITH - 1.14.6 diff --git a/README.md b/README.md index 7db80e4c..90bc1d25 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,165 @@ -# README +## Introduction -This README would normally document whatever steps are necessary to get the -application up and running. +VisCodex is for building models of the physical collation of manuscripts, and then visualizing them in various ways. The VisCodex project is led by Dot Porter at the [Schoenberg Institute for Manuscript Studies](https://schoenberginstitute.org/) at the University of Pennsylvania, in collaboration with the [University of Toronto Libraries](https://onesearch.library.utoronto.ca/about) and the [Old Books New Science lab](https://oldbooksnewscience.com/). Collaborators include Alexandra Gillespie, Alberto Campagnolo, and Conal Tuohy. -Things you may want to cover: +## System Requirements -* Ruby version +- `rvm` (>= 1.29.1) +- `ruby` (>= 2.4.1) +- `node` (>= 6.11.4) +- `npm` (>= 3.10.10) -* System dependencies +### Additional Requirements for Development: -* Configuration +- [`mailcatcher`](https://mailcatcher.me/) (>= 0.6.5) +- [Redux DevTools for Firefox or Chrome](https://github.com/zalmoxisus/redux-devtools-extension) (>= 2.15.1) -* Database creation +## Development setup with Docker -* Database initialization +Instead of manually installing the dependencies locally on your machine for development, you can use Docker with the provided Dockerfile and docker-compose.yml. -* How to run the test suite +Update the mongo host name on line 12 in `viscoll-api/config/mongoid.yml` from `localhost` to `mongo` (this is the Docker service name defined in docker-compose.yml). -* Services (job queues, cache servers, search engines, etc.) +Bring up the containers with: -* Deployment instructions +``` +docker-compose up +``` -* ... +To access emails being sent by the app (for user account activation, password reset, etc), set up Ethereal with the following credentials: + +``` +:user_name => 'libby.corkery17@ethereal.email', +:password => 'RP4P6zMm3rVW9adMZF' +``` +This configuration is located at `viscoll-api/config/environments/development.rb`. + +## Installation and Setup + +Skip this section if you are using Docker for development. + +### VisCodex API (Rails) + +Rails-driven back-end for VisCodex + +#### System Requirements + +- `rvm` (>= 1.29.1) +- `ruby` (>= 2.4.1) + +##### Additional Requirements for Development: + +- [`mailcatcher`](https://mailcatcher.me/) (>= 0.6.5) + +#### Setup + +Run the following commands to install the dependencies: +``` +rvm --ruby-version use 2.4.1@viscollobns +bundle install +``` + +Set the admin email address in two locations: + +`viscoll-api/app/mailers/mailer.rb` on line 18: + +``` +toEmail = Rails.application.secrets.admin_email || "dummy-admin@library.utoronto.ca" +``` + +and `viscoll-api/app/mailers/feedback_mailer.rb` on line 10: + +``` +to:"utlviscoll@library.utoronto.ca", +``` + +Then run this to start the API server: +``` +rails s -p 3001 +``` + +If you wish to receive confirmation and password reset emails while developing, also start the mailcatcher daemon: +``` +mailcatcher +``` + +#### Testing + +Run this command to test once: +``` +rspec +``` + +Alternatively, run this command to test continually while monitoring for changes: +``` +guard +``` + +### VisCodex App (React-Redux) + +Redux-driven user interface for VisCodex + +#### System Requirements + +- `node` (>= 6.11.4) +- `npm` (>= 3.10.10) + +##### Additional Requirements for Development: + +- [Redux DevTools for Firefox or Chrome](https://github.com/zalmoxisus/redux-devtools-extension) (>= 2.15.1) + +#### Setup + +Run this to install the dependencies: +``` +npm install +``` + +Then run the dev server which brings up a browser window serving the user interface: +``` +npm start +``` + +#### Testing + +Run this command to test once: +``` +npm test +``` + +Alternatively, run this command to test continually while monitoring for changes: +``` +npm test -- --watch +``` + +#### Building + +Before building the app, edit line 3 in `viscoll-app/src/store/axiosConfig.js` to contain the correct root endpoint of the VisCodex API: + +```Javascript +export let API_URL = '/api'; + +``` + +Build the app with: +``` +npm build +``` + + + +## Copyright and License + +Copyright 2020 University of Toronto Libraries + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js deleted file mode 100644 index b16e53d6..00000000 --- a/app/assets/config/manifest.js +++ /dev/null @@ -1,3 +0,0 @@ -//= link_tree ../images -//= link_directory ../javascripts .js -//= link_directory ../stylesheets .css diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index fb35a858..00000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,16 +0,0 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. JavaScript code in this file should be added after the last require_* statement. -// -// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details -// about supported directives. -// -//= require jquery -//= require jquery_ujs -//= require bootstrap-sprockets -//= require_tree . diff --git a/app/assets/javascripts/cable.js b/app/assets/javascripts/cable.js deleted file mode 100644 index 71ee1e66..00000000 --- a/app/assets/javascripts/cable.js +++ /dev/null @@ -1,13 +0,0 @@ -// Action Cable provides the framework to deal with WebSockets in Rails. -// You can generate new channels where WebSocket features live using the rails generate channel command. -// -//= require action_cable -//= require_self -//= require_tree ./channels - -(function() { - this.App || (this.App = {}); - - App.cable = ActionCable.createConsumer(); - -}).call(this); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss deleted file mode 100644 index 96890d08..00000000 --- a/app/assets/stylesheets/application.scss +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS - * files in this directory. Styles in this file should be added after the last require_* statement. - * It is generally better to create a new file per style scope. - * - *= require_self - *= require_tree . - */ - -@import "bootstrap-sprockets"; -@import "bootstrap"; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb deleted file mode 100644 index 1c07694e..00000000 --- a/app/controllers/application_controller.rb +++ /dev/null @@ -1,3 +0,0 @@ -class ApplicationController < ActionController::Base - protect_from_forgery with: :exception -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb deleted file mode 100644 index de6be794..00000000 --- a/app/helpers/application_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ApplicationHelper -end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb deleted file mode 100644 index a69ba30d..00000000 --- a/app/views/layouts/application.html.erb +++ /dev/null @@ -1,14 +0,0 @@ - - - - ViscollObns - <%= csrf_meta_tags %> - - <%= stylesheet_link_tag 'application', media: 'all' %> - <%= javascript_include_tag 'application' %> - - - - <%= yield %> - - diff --git a/bin/rake b/bin/rake deleted file mode 100755 index 17240489..00000000 --- a/bin/rake +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index 01ef3e66..00000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,11 +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 app/assets folder are already added. -# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb deleted file mode 100644 index 5a6a32d3..00000000 --- a/config/initializers/cookies_serializer.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Specify a serializer for the signed and encrypted cookie jars. -# Valid options are :json, :marshal, and :hybrid. -Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb deleted file mode 100644 index 08f5a991..00000000 --- a/config/initializers/session_store.rb +++ /dev/null @@ -1,3 +0,0 @@ -# Be sure to restart your server when you modify this file. - -Rails.application.config.session_store :cookie_store, key: '_ViscollObns_session' diff --git a/config/routes.rb b/config/routes.rb deleted file mode 100644 index 787824f8..00000000 --- a/config/routes.rb +++ /dev/null @@ -1,3 +0,0 @@ -Rails.application.routes.draw do - # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html -end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..9debd861 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ +version: '3.7' + +services: + api: + build: . + image: viscoll + container_name: viscoll-api + volumes: + - ./viscoll-api:/app + working_dir: /app + command: bash -c "rm -f tmp/pids/server.pid && bundle i && bundle exec rails s -p 3001 -b '0.0.0.0'" + ports: + - 3001:3001 + depends_on: + - mongo + - mongo-express + + app: + build: . + image: viscoll + container_name: viscoll-app + volumes: + - ./viscoll-app:/app + working_dir: /app + command: bash -c "npm install && npm start" + ports: + - 3000:3000 + depends_on: + - api + + mongo: + container_name: viscoll-mongo + image: mongo:4.0 + volumes: + - mongo:/data/db + + mongo-express: + image: mongo-express + container_name: viscoll-mongo-express + ports: + - 127.0.0.1:3002:8081 + depends_on: + - mongo + environment: + ME_CONFIG_MONGODB_SERVER: mongo + depends_on: + - mongo + +volumes: + mongo: diff --git a/lib/tasks/.keep b/lib/tasks/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/log/.keep b/log/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/public/404.html b/public/404.html deleted file mode 100644 index b612547f..00000000 --- a/public/404.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The page you were looking for doesn't exist (404) - - - - - - -
-
-

The page you were looking for doesn't exist.

-

You may have mistyped the address or the page may have moved.

-
-

If you are the application owner check the logs for more information.

-
- - diff --git a/public/422.html b/public/422.html deleted file mode 100644 index a21f82b3..00000000 --- a/public/422.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The change you wanted was rejected (422) - - - - - - -
-
-

The change you wanted was rejected.

-

Maybe you tried to change something you didn't have access to.

-
-

If you are the application owner check the logs for more information.

-
- - diff --git a/public/500.html b/public/500.html deleted file mode 100644 index 061abc58..00000000 --- a/public/500.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - We're sorry, but something went wrong (500) - - - - - - -
-
-

We're sorry, but something went wrong.

-
-

If you are the application owner check the logs for more information.

-
- - diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png deleted file mode 100644 index e69de29b..00000000 diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png deleted file mode 100644 index e69de29b..00000000 diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index e69de29b..00000000 diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index 3c9c7c01..00000000 --- a/public/robots.txt +++ /dev/null @@ -1,5 +0,0 @@ -# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file -# -# To ban all spiders from the entire site uncomment the next two lines: -# User-agent: * -# Disallow: / diff --git a/tmp/.keep b/tmp/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/assets/javascripts/.keep b/vendor/assets/javascripts/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/assets/stylesheets/.keep b/vendor/assets/stylesheets/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/.rspec b/viscoll-api/.rspec similarity index 56% rename from .rspec rename to viscoll-api/.rspec index 83e16f80..4e33a322 100644 --- a/.rspec +++ b/viscoll-api/.rspec @@ -1,2 +1,3 @@ ---color --require spec_helper +--color +--format documentation diff --git a/Gemfile b/viscoll-api/Gemfile similarity index 56% rename from Gemfile rename to viscoll-api/Gemfile index 6325e251..6da243cd 100644 --- a/Gemfile +++ b/viscoll-api/Gemfile @@ -7,26 +7,15 @@ end # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 5.0.2' +gem 'rails', '5.2.5.0' # Use Puma as the app server gem 'puma', '~> 3.0' -# Use SCSS for stylesheets -gem 'sass-rails', '~> 5.0' -# Use Uglifier as compressor for JavaScript assets -gem 'uglifier', '>= 1.3.0' -# Use CoffeeScript for .coffee assets and views -# gem 'coffee-rails', '~> 4.2' -# See https://github.com/rails/execjs#readme for more supported runtimes -# gem 'therubyracer', platforms: :ruby - -# Use jquery as the JavaScript library -gem 'jquery-rails' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 2.5' +gem 'jbuilder', '~> 2.7' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 3.0' # Use ActiveModel has_secure_password -gem 'bcrypt', '~> 3.1.7' +# gem 'bcrypt', '~> 3.1.7' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development @@ -34,14 +23,19 @@ gem 'bcrypt', '~> 3.1.7' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri - gem 'factory_girl_rails' - gem 'rspec-rails' - gem 'faker' + gem 'rspec-rails', '~> 3.8.2' + gem 'factory_girl_rails', '~> 4.8' + gem 'shoulda-matchers', '~> 3.1', '>= 3.1.1' + gem 'faker', '~> 1.7', '>= 1.7.3' + gem 'database_cleaner', '~> 1.6', '>= 1.6.1' + gem 'simplecov', :require => false + gem 'mongoid-rspec', github: 'mongoid-rspec/mongoid-rspec' + gem 'guard-rspec' + gem 'rspec_junit_formatter', '~> 0.3.0' + gem 'webmock', '~> 3.1.0' end group :development do - # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. - gem 'web-console', '>= 3.3.0' gem 'listen', '~> 3.0.5' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' @@ -51,18 +45,10 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'mongoid', '~> 6.2' +gem 'rails_jwt_auth', '0.16.1' +gem "shrine" +gem 'rubyzip', '1.3.0' -gem 'devise' -gem 'mongoid' -gem 'bootstrap-sass' - - -group :test do - gem 'rspec' - gem 'mongoid-rspec' - gem 'capybara' - gem 'cucumber-rails', require: false - gem 'database_cleaner' - gem 'email_spec' - gem 'launchy' -end +# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible +gem 'rack-cors', '1.1.1' diff --git a/viscoll-api/Gemfile.lock b/viscoll-api/Gemfile.lock new file mode 100644 index 00000000..49edf36c --- /dev/null +++ b/viscoll-api/Gemfile.lock @@ -0,0 +1,272 @@ +GIT + remote: https://github.com/mongoid-rspec/mongoid-rspec.git + revision: fbbed8f9b63f8479ca5983835e20080e95ddc9db + specs: + mongoid-rspec (4.1.1) + activesupport (>= 3.0.0) + mongoid (>= 3.1) + mongoid-compatibility (>= 0.5.1) + rspec-core (~> 3.3) + rspec-expectations (~> 3.3) + rspec-mocks (~> 3.3) + +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.2.5) + actionpack (= 5.2.5) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.5) + actionpack (= 5.2.5) + actionview (= 5.2.5) + activejob (= 5.2.5) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.2.5) + actionview (= 5.2.5) + activesupport (= 5.2.5) + rack (~> 2.0, >= 2.0.8) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.2.5) + activesupport (= 5.2.5) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.2.5) + activesupport (= 5.2.5) + globalid (>= 0.3.6) + activemodel (5.2.5) + activesupport (= 5.2.5) + activerecord (5.2.5) + activemodel (= 5.2.5) + activesupport (= 5.2.5) + arel (>= 9.0) + activestorage (5.2.5) + actionpack (= 5.2.5) + activerecord (= 5.2.5) + marcel (~> 1.0.0) + activesupport (5.2.5) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + arel (9.0.0) + bcrypt (3.1.16) + bson (4.12.0) + builder (3.2.4) + byebug (11.1.3) + coderay (1.1.3) + concurrent-ruby (1.1.8) + content_disposition (1.0.0) + crack (0.4.5) + rexml + crass (1.0.6) + database_cleaner (1.99.0) + diff-lcs (1.4.4) + docile (1.3.5) + down (5.2.1) + addressable (~> 2.5) + erubi (1.10.0) + factory_girl (4.9.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.9.0) + factory_girl (~> 4.9.0) + railties (>= 3.0.0) + faker (1.9.6) + i18n (>= 0.7) + ffi (1.15.0) + formatador (0.2.5) + globalid (0.4.2) + activesupport (>= 4.2.0) + guard (2.16.2) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (>= 1.0.12, < 2.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-rspec (4.7.3) + guard (~> 2.1) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) + hashdiff (1.0.1) + i18n (1.8.10) + concurrent-ruby (~> 1.0) + jbuilder (2.11.2) + activesupport (>= 5.0.0) + jwt (1.5.6) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + loofah (2.9.1) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + lumberjack (1.2.8) + mail (2.7.1) + mini_mime (>= 0.1.1) + marcel (1.0.1) + method_source (1.0.0) + mini_mime (1.1.0) + mini_portile2 (2.5.1) + minitest (5.14.4) + mongo (2.14.0) + bson (>= 4.8.2, < 5.0.0) + mongoid (6.4.8) + activemodel (>= 5.1, < 6.0.0) + mongo (>= 2.5.1, < 3.0.0) + mongoid-compatibility (0.5.1) + activesupport + mongoid (>= 2.0) + nenv (0.3.0) + nio4r (2.5.7) + nokogiri (1.11.3) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + notiffany (0.1.3) + nenv (~> 0.1) + shellany (~> 0.0) + pry (0.14.1) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (4.0.6) + puma (3.12.6) + racc (1.5.2) + rack (2.2.3) + rack-cors (1.1.1) + rack (>= 2.0.0) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.2.5) + actioncable (= 5.2.5) + actionmailer (= 5.2.5) + actionpack (= 5.2.5) + actionview (= 5.2.5) + activejob (= 5.2.5) + activemodel (= 5.2.5) + activerecord (= 5.2.5) + activestorage (= 5.2.5) + activesupport (= 5.2.5) + bundler (>= 1.3.0) + railties (= 5.2.5) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.3.0) + loofah (~> 2.3) + rails_jwt_auth (0.16.1) + bcrypt (~> 3.1) + jwt (~> 1.5) + rails (~> 5.0) + warden (~> 1.2) + railties (5.2.5) + actionpack (= 5.2.5) + activesupport (= 5.2.5) + method_source + rake (>= 0.8.7) + thor (>= 0.19.0, < 2.0) + rake (13.0.3) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.5) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.2) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-rails (3.8.3) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.3) + rspec_junit_formatter (0.3.0) + rspec-core (>= 2, < 4, != 2.12.0) + rubyzip (1.3.0) + shellany (0.0.1) + shoulda-matchers (3.1.3) + activesupport (>= 4.0.0) + shrine (3.3.0) + content_disposition (~> 1.0) + down (~> 5.1) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.3) + spring (2.1.1) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) + sprockets (4.0.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.2) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + thor (1.1.0) + thread_safe (0.3.6) + tzinfo (1.2.9) + thread_safe (~> 0.1) + warden (1.2.9) + rack (>= 2.0.9) + webmock (3.1.1) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff + websocket-driver (0.7.3) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + +PLATFORMS + ruby + +DEPENDENCIES + byebug + database_cleaner (~> 1.6, >= 1.6.1) + factory_girl_rails (~> 4.8) + faker (~> 1.7, >= 1.7.3) + guard-rspec + jbuilder (~> 2.7) + listen (~> 3.0.5) + mongoid (~> 6.2) + mongoid-rspec! + puma (~> 3.0) + rack-cors (= 1.1.1) + rails (= 5.2.5.0) + rails_jwt_auth (= 0.16.1) + rspec-rails (~> 3.8.2) + rspec_junit_formatter (~> 0.3.0) + rubyzip (= 1.3.0) + shoulda-matchers (~> 3.1, >= 3.1.1) + shrine + simplecov + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + webmock (~> 3.1.0) + +BUNDLED WITH + 2.1.4 diff --git a/viscoll-api/Guardfile b/viscoll-api/Guardfile new file mode 100644 index 00000000..08a70b17 --- /dev/null +++ b/viscoll-api/Guardfile @@ -0,0 +1,14 @@ +guard :rspec, cmd: "bundle exec rspec" do + require "guard/rspec/dsl" + dsl = Guard::RSpec::Dsl.new(self) + + # RSpec files + rspec = dsl.rspec + watch(rspec.spec_files) + + # Rails files + watch(%r{^app/controllers/*}) { rspec.spec_dir } + watch(%r{^app/models/*}) { rspec.spec_dir } + watch(%r{^app/views/*}) { rspec.spec_dir } + watch(%r{^app/config/*}) { rspec.spec_dir } +end diff --git a/viscoll-api/README.md b/viscoll-api/README.md new file mode 100644 index 00000000..5c6dfa07 --- /dev/null +++ b/viscoll-api/README.md @@ -0,0 +1,58 @@ +# VisColl (Rails API Back-End) + +## Introduction + +This is the the Rails-driven back-end for Viscoll. + +## System Requirements + +- `rvm` (>= 1.29.1) +- `ruby` (>= 2.4.1) + +### Additional Requirements for Development: + +- [`mailcatcher`](https://mailcatcher.me/) (>= 0.6.5) + +## Setup + +Run the following commands to install the dependencies: +``` +rvm --ruby-version use 2.4.1@viscollobns +bundle install +``` + +Set the admin email address in two locations: + +`viscoll-api/app/mailers/mailer.rb` on line 18: + +``` +toEmail = Rails.application.secrets.admin_email || "dummy-admin@library.utoronto.ca" +``` + +and `viscoll-api/app/mailers/feedback_mailer.rb` on line 10: + +``` +to:"utlviscoll@library.utoronto.ca", +``` + +Then run this to start the API server: +``` +rails s -p 3001 +``` + +If you wish to receive confirmation and password reset emails while developing, also start the mailcatcher daemon: +``` +mailcatcher +``` + +## Testing + +Run this command to test once: +``` +rspec +``` + +Alternatively, run this command to test continually while monitoring for changes: +``` +guard +``` diff --git a/Rakefile b/viscoll-api/Rakefile similarity index 100% rename from Rakefile rename to viscoll-api/Rakefile diff --git a/app/channels/application_cable/channel.rb b/viscoll-api/app/channels/application_cable/channel.rb similarity index 100% rename from app/channels/application_cable/channel.rb rename to viscoll-api/app/channels/application_cable/channel.rb diff --git a/app/channels/application_cable/connection.rb b/viscoll-api/app/channels/application_cable/connection.rb similarity index 100% rename from app/channels/application_cable/connection.rb rename to viscoll-api/app/channels/application_cable/connection.rb diff --git a/viscoll-api/app/controllers/application_controller.rb b/viscoll-api/app/controllers/application_controller.rb new file mode 100644 index 00000000..553e9438 --- /dev/null +++ b/viscoll-api/app/controllers/application_controller.rb @@ -0,0 +1,19 @@ +class ApplicationController < ActionController::API + before_action :set_base_api_url + def set_base_api_url + @base_api_url = Rails.application.secrets.api_url ? Rails.application.secrets.api_url : 'https://dummy.library.utoronto.ca/api' + end + + include RailsJwtAuth::WardenHelper + include ControllerHelper::ProjectsHelper + include ControllerHelper::GroupsHelper + include ControllerHelper::LeafsHelper + include ControllerHelper::FilterHelper + include ControllerHelper::ImportJsonHelper + include ControllerHelper::ImportXmlHelper + include ControllerHelper::ImportMappingHelper + include ControllerHelper::ExportHelper + include ValidationHelper::ProjectValidationHelper + include ValidationHelper::GroupValidationHelper + include ValidationHelper::LeafValidationHelper +end diff --git a/app/assets/images/.keep b/viscoll-api/app/controllers/concerns/.keep similarity index 100% rename from app/assets/images/.keep rename to viscoll-api/app/controllers/concerns/.keep diff --git a/viscoll-api/app/controllers/concerns/rails_jwt_auth/warden_helper.rb b/viscoll-api/app/controllers/concerns/rails_jwt_auth/warden_helper.rb new file mode 100644 index 00000000..9b4b40e9 --- /dev/null +++ b/viscoll-api/app/controllers/concerns/rails_jwt_auth/warden_helper.rb @@ -0,0 +1,36 @@ +module RailsJwtAuth + module WardenHelper + def signed_in? + !current_user.nil? + end + + def current_user + warden.user + end + + def warden + request.env['warden'] + end + + def authenticate! + begin + warden.authenticate!(store: false) + rescue Exception => e + render json: {error: "Authorization Token: "+e.message}, status: :bad_request + return false + end + end + + def authenticateDestroy! + warden.authenticate!(store: false) + end + + def self.included(base) + return unless Rails.env.test? && base.name == 'ApplicationController' + + base.send(:rescue_from, RailsJwtAuth::Spec::NotAuthorized) do + render json: {}, status: 401 + end + end + end +end diff --git a/viscoll-api/app/controllers/confirmations_controller.rb b/viscoll-api/app/controllers/confirmations_controller.rb new file mode 100644 index 00000000..4bf38439 --- /dev/null +++ b/viscoll-api/app/controllers/confirmations_controller.rb @@ -0,0 +1,24 @@ +class ConfirmationsController < ApplicationController + def update + if params[:confirmation_token].blank? + return render_422(confirmation_token: [I18n.t('rails_jwt_auth.errors.not_found')]) + end + user = RailsJwtAuth.model.where(confirmation_token: params[:confirmation_token]).first + return render_422(confirmation_token: [I18n.t('rails_jwt_auth.errors.not_found')]) unless user + if user.confirm! + AccountApprovalMailer.sendApprovalStatus(user).deliver_now + render_204 + else + render_422(user.errors) + end + end + + def render_204 + render json: {}, status: 204 + end + + def render_422(errors) + render json: {errors: errors}, status: 422 + end + +end diff --git a/viscoll-api/app/controllers/export_controller.rb b/viscoll-api/app/controllers/export_controller.rb new file mode 100644 index 00000000..1c2aea4a --- /dev/null +++ b/viscoll-api/app/controllers/export_controller.rb @@ -0,0 +1,70 @@ +require 'zip' + +class ExportController < ApplicationController + before_action :authenticate! + before_action :set_project, only: [:show] + + # GET /projects/:id/export/:format + def show + # Zip all DIY images and provide the link to download the file + begin + @zipFilePath = nil + images = [] + current_user.images.all.each do |image| + if image.projectIDs.include? @project.id.to_s + images.push(image) + end + end + if !images.empty? + basePath = "#{Rails.root}/public/uploads/" + zipFilename = "#{basePath}#{@project.id.to_s}_images.zip" + File.delete(zipFilename) if File.exist?(zipFilename) + ::Zip::File.open(zipFilename, Zip::File::CREATE) do |zipFile| + images.each do |image| + fileExtension = image.metadata['mime_type'].split('/')[1] + filenameOnly = image.filename.rpartition(".")[0] + zipFile.add("#{filenameOnly}_#{image.fileID}.#{fileExtension}", "#{basePath}#{image.fileID}") + end + end + @zipFilePath = "#{@base_api_url}/images/zip/#{@project.id.to_s}" + end + rescue Exception => e + end + + begin + case @format + when "xml" + exportData = buildDotModel(@project) + xml = Nokogiri::XML(exportData) + schema = Nokogiri::XML::RelaxNG(File.open("public/viscoll-datamodel2.rng")) + errors = schema.validate(xml) + if errors.empty? + render json: {data: exportData, type: @format, Images: {exportedImages:@zipFilePath ? @zipFilePath : false}}, status: :ok and return + else + render json: {data: errors, type: @format}, status: :unprocessable_entity and return + end + when "json" + @data = buildJSON(@project) + render :'exports/show', status: :ok and return + else + render json: {error: "Export format must be one of [json, xml]"}, status: :unprocessable_entity and return + end + rescue Exception => e + render json: {error: e.message}, status: :internal_server_error and return + end + end + + private + def set_project + begin + @project = Project.find(params[:id]) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + @format = params[:format] + rescue Exception => e + render json: {error: "project not found with id "+params[:id]}, status: :not_found and return + end + end + +end diff --git a/viscoll-api/app/controllers/feedback_controller.rb b/viscoll-api/app/controllers/feedback_controller.rb new file mode 100644 index 00000000..64a0badb --- /dev/null +++ b/viscoll-api/app/controllers/feedback_controller.rb @@ -0,0 +1,34 @@ +class FeedbackController < ApplicationController + before_action :authenticate! + + # POST /feedback + def create + begin + if not current_user + render json: {}, status: :unprocessable_entity and return + end + @title = feedback_params[:title] + @message = feedback_params[:message] + @browserInformation = feedback_params[:browserInformation] + @projectJSONExport = feedback_params[:project] + if @title.blank? or @message.blank? + render json: {error: "[title] and [message] params required."}, status: :unprocessable_entity and return + end + FeedbackMailer.sendFeedback( + @title, + @message, + @browserInformation, + @projectJSONExport, + current_user + ).deliver_now + render json: {}, status: :ok and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + private + def feedback_params + params.require(:feedback).permit(:title, :message, :browserInformation, :project) + end +end diff --git a/viscoll-api/app/controllers/filter_controller.rb b/viscoll-api/app/controllers/filter_controller.rb new file mode 100644 index 00000000..f87fdc38 --- /dev/null +++ b/viscoll-api/app/controllers/filter_controller.rb @@ -0,0 +1,219 @@ +class FilterController < ApplicationController + before_action :authenticate! + before_action :set_project, only: [:show] + + # PUT /projects/filter + def show + begin + queries = filter_params.to_h[:queries] + errors = runValidations(queries) + if errors != [] + render json: {errors: errors}, status: :unprocessable_entity and return + end + @objectIDs = {Groups: [], Leafs: [], Sides: [], Notes: []} + @visibleAttributes = { + group: {type:false, title:false}, + leaf: {type:false, material:false, conjoined_leaf_order:false, attached_below:false, attached_above:false, stub:false}, + side: {folio_number:false, texture:false, script_direction:false, uri:false} + } + combinedResult = performFilter(queries) + finalResponse = buildResponse(combinedResult) + @groups = finalResponse[:Groups] + @leafs = finalResponse[:Leafs] + @sides = finalResponse[:Sides] + @notes = finalResponse[:Notes] + @groupsOfMatchingLeafs = finalResponse[:GroupsOfMatchingLeafs] + @leafsOfMatchingSides = finalResponse[:LeafsOfMatchingSides] + @groupsOfMatchingSides = finalResponse[:GroupsOfMatchingSides] + @groupsOfMatchingNotes = finalResponse[:GroupsOfMatchingNotes] + @leafsOfMatchingNotes = finalResponse[:LeafsOfMatchingNotes] + @sidesOfMatchingNotes = finalResponse[:SidesOfMatchingNotes] + if @groups == [] + @visibleAttributes[:group] = {type:false, title:false} + end + if @leafs == [] + @visibleAttributes[:leaf] = {type:false, material:false, conjoined_leaf_order:false, attached_below:false, attached_above:false, stub:false} + end + if @sides == [] + @visibleAttributes[:side] = {folio_number:false, texture:false, script_direction:false, uri:false} + end + rescue Exception => e + render json: {errors: e.message}, status: :unprocessable_entity and return + end + end + + + + def performFilter(queries) + sets = [] + conjunctions = [] + queries.each do |query| + type = query[:type] + old_attribute = nil + attribute = query[:attribute] + condition = query[:condition] + values = query[:values] + conjunction = query[:conjunction] + groups = [] + leafs = [] + sides = [] + notes = [] + + if attribute == 'conjoined_leaf_order' + old_attribute = attribute + attribute = 'conjoined_to' + values = values.map { |val| val=="None" ? nil : val } + end + if attribute == 'conjoined_to' + values = values.map { |val| val=="None" ? nil : val } + end + + query_condition_params = { attribute => { '$in': [] } } + + case condition + when 'equals' + query_condition_params = { attribute => (values.length > 1) ? { '$in': values } : values[0] } + when 'not equals' + query_condition_params = { attribute => (values.length > 1) ? { '$nin': values } : { '$ne': values[0] } } + when 'contains' + query_condition_params = { attribute => (values.length > 1) ? { '$in': values.map { |x| /^#{Regexp.escape(x)}/} } : /#{Regexp.escape(values[0])}/ } + when 'not contains' + query_condition_params = { attribute => (values.length > 1) ? { '$nin': values.map { |x| /^#{Regexp.escape(x)}/} } : { '$not': /#{Regexp.escape(values[0])}/} } + end + + case type + when 'group' + groupQueryResult = @project.groups.only(:id).where(query_condition_params) + groups = groupQueryResult.collect { |gqr| gqr.id.to_s } + @objectIDs[:Groups] += groups + if groups.length > 0 + @visibleAttributes[:group][attribute] = true + end + when 'leaf' + leafQueryResult = @project.leafs.only(:id).where(query_condition_params) + leafs = leafQueryResult.collect { |lqr| lqr.id.to_s } + if leafs.length > 0 + if old_attribute + @visibleAttributes[:leaf][old_attribute] = true + else + @visibleAttributes[:leaf][attribute] = true + end + end + @objectIDs[:Leafs] += leafs + when 'side' + sideQueryResult = @project.sides.only(:id).where(query_condition_params) + sides = sideQueryResult.collect { |sqr| sqr.id.to_s } + sideQueryResult.each do |sideID| + sides.push(sideID.id.to_s) + end + if sides.length > 0 + @visibleAttributes[:side][attribute] = true + end + @objectIDs[:Sides] += sides + when 'note' + noteQueryResult = @project.notes.only(:id).where(query_condition_params) + notes = noteQueryResult.collect { |nqr| nqr.id.to_s } + @objectIDs[:Notes] += notes + end + sets.push(Set.new([*groups, *leafs, *sides, *notes])) + conjunctions.push(conjunction) + end + conjunctions.pop + result = sets[0] + conjunctions.each_with_index do |conjunction, index| + if (index+1 <= sets.length-1) + if conjunction == "AND" + result = result & sets[index+1] + else + result = result | sets[index+1] + end + end + end + return result + end + + + def buildResponse(combinedResult) + response = {Groups: [], Leafs: [], Sides: [], Notes: [], GroupsOfMatchingNotes: [], LeafsOfMatchingNotes: [], SidesOfMatchingNotes:[], LeafsOfMatchingSides:[], GroupsOfMatchingSides:[], GroupsOfMatchingLeafs:[]} + combinedResult.each do |objectID| + if @objectIDs[:Groups].include?(objectID) + response[:Groups].push(objectID) + elsif @objectIDs[:Leafs].include?(objectID) + response[:Leafs].push(objectID) + elsif @objectIDs[:Sides].include?(objectID) + response[:Sides].push(objectID) + elsif @objectIDs[:Notes].include?(objectID) + note = Note.find(objectID) + groupIDs = note.objects[:Group] + leafIDs = note.objects[:Leaf] + rectoIDs = note.objects[:Recto] + versoIDs = note.objects[:Verso] + groupIDs.each do |groupID| + if !(response[:Groups].include?(groupID)) + response[:Groups].push(groupID) + response[:GroupsOfMatchingNotes].push(groupID) + end + end + leafIDs.each do |leafID| + if !(response[:Leafs].include?(leafID)) + response[:Leafs].push(leafID) + response[:LeafsOfMatchingNotes].push(leafID) + end + end + rectoIDs.each do |sideID| + if !(response[:Sides].include?(sideID)) + response[:Sides].push(sideID) + response[:SidesOfMatchingNotes].push(sideID) + end + end + versoIDs.each do |sideID| + if !(response[:Sides].include?(sideID)) + response[:Sides].push(sideID) + response[:SidesOfMatchingNotes].push(sideID) + end + end + response[:Notes].push(objectID) + end + end + response[:Sides].each do |sideID| + leafID = Side.find(sideID).parentID + if (!(response[:LeafsOfMatchingSides].include?(leafID)) and !(@objectIDs[:Leafs].include?(leafID))) + response[:LeafsOfMatchingSides].push(leafID) + end + end + response[:LeafsOfMatchingSides].each do |leafID| + groupID = Leaf.find(leafID).parentID + if (!(response[:GroupsOfMatchingSides].include?(groupID)) and !(@objectIDs[:Groups].include?(groupID))) + response[:GroupsOfMatchingSides].push(groupID) + end + end + response[:Leafs].each do |leafID| + groupID = Leaf.find(leafID).parentID + if (!(response[:GroupsOfMatchingLeafs].include?(groupID)) and !(@objectIDs[:Groups].include?(groupID))) + response[:GroupsOfMatchingLeafs].push(groupID) + end + end + return response + end + + + private + # Use callbacks to share common setup or constraints between actions. + def set_project + begin + @project = Project.find(params[:id]) + if (@project.user_id!=current_user.id) + render status: :unauthorized and return + end + rescue Exception => e + render json: {error: "project not found with id "+params[:id]}, status: :not_found and return + end + end + + # Never trust parameters from the scary internet, only allow the white list through. + def filter_params + params.permit(:queries => [:type, :attribute, :condition, :conjunction, :values => []]) + end + + +end diff --git a/viscoll-api/app/controllers/groups_controller.rb b/viscoll-api/app/controllers/groups_controller.rb new file mode 100644 index 00000000..01db43f4 --- /dev/null +++ b/viscoll-api/app/controllers/groups_controller.rb @@ -0,0 +1,186 @@ +class GroupsController < ApplicationController + before_action :authenticate! + before_action :set_group, only: [:update, :destroy] + + # POST /groups + def create + begin + noOfGroups = additional_params.to_h[:noOfGroups] + memberOrder = additional_params.to_h[:memberOrder] + parentGroupID = additional_params.to_h[:parentGroupID] + noOfLeafs = additional_params.to_h[:noOfLeafs] + conjoin = additional_params.to_h[:conjoin] + oddMemberLeftOut = additional_params.to_h[:oddMemberLeftOut] + groupIDs = additional_params.to_h[:groupIDs] + leafIDs = additional_params.to_h[:leafIDs] + sideIDs = additional_params.to_h[:sideIDs] + project_id = group_params.to_h[:project_id] + order = additional_params.to_h[:order] + direction = group_params.to_h[:direction] + # Validate group parameters + @additionalErrors = validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLeafs, conjoin, oddMemberLeftOut) + hasAdditionalErrors = false + @additionalErrors.each_value do |value| + if value.length>0 + hasAdditionalErrors = true + end + end + if (hasAdditionalErrors) + render json: {additional: @additionalErrors}, status: :unprocessable_entity and return + end + @groupErrors = {project_id: []} + if (project_id == nil) + @groupErrors[:project_id].push("not found") + render json: {group: @groupErrors}, status: :unprocessable_entity and return + end + begin + @project = Project.find(project_id) + rescue Exception => e + @groupErrors[:project_id].push("project not found with id "+project_id) + render json: {group: @groupErrors}, status: :unprocessable_entity and return + end + new_groups = [] + new_group_ids = [] + groupIDIndex = 0 + parent_group = nil + if parentGroupID != nil + parent_group = @project.groups.find(parentGroupID) + end + # Create groups + noOfGroups.times do |i| + group = Group.new(group_params) + if groupIDs + group.id = groupIDs[i] + end + if parentGroupID != nil + group.parentID = parentGroupID + group.nestLevel = parent_group.nestLevel + 1 + end + if group.save + new_groups.push(group) + new_group_ids.push(group.id.to_s) + else + render json: {group: group.errors}, status: :unprocessable_entity and return + end + end + # Add new group(s) to parent + if parentGroupID != nil + parent_group.add_members(new_group_ids, memberOrder) + end + # Add group(s) to global list + @project.add_groupIDs(new_group_ids, order.to_i - 1) + # Add leaves inside each new group + new_groups.each_with_index do |group, index| + if noOfLeafs + if (leafIDs and sideIDs) + addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut, leafIDs[index*noOfLeafs..index*noOfLeafs+noOfLeafs-1], sideIDs[index*2*noOfLeafs..index*2*noOfLeafs+noOfLeafs*2-1]) + else + addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut) + end + end + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + # PATCH/PUT /groups/1 + def update + begin + if !@group.update(group_params) + render json: @group.errors, status: :unprocessable_entity and return + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + # PATCH/PUT /groups + def updateMultiple + begin + allGroups = group_params_batch_update.to_h[:groups] + # Run validations + errors = validateGroupBatchUpdate(allGroups) + if not errors.empty? + render json: {groups: errors}, status: :unprocessable_entity and return + end + allGroups.each do |group_params| + @group = Group.find(group_params[:id]) + @project = Project.find(@group.project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + if !@group.update(group_params[:attributes]) + render json: @group.errors, status: :unprocessable_entity and return + end + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + # DELETE /groups/1 + def destroy + begin + @group = Group.find(params[:id]) + @group.destroy + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + # DELETE /groups + def destroyMultiple + begin + groupIDs = group_params_batch_delete.to_h[:groups] + projectID = group_params_batch_delete.to_h[:projectID] + # Delete groups + groupIDs.each do |groupID| + # Wrapping destroy in begin/rescue because group may no longer exist when it's nested + begin + group = Group.find(groupID) + @project = Project.find(group.project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + group.destroy + rescue Exception => e + next + end + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + private + def set_group + begin + @group = Group.find(params[:id]) + @project = Project.find(@group.project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + rescue Exception => e + render json: {error: "group not found"}, status: :not_found and return + end + end + + def group_params + params.require(:group).permit(:project_id, :type, :title, :direction, :tacketed=>[], :sewing=>[]) + end + + def additional_params + params.require(:additional).permit(:order, :noOfGroups, :memberOrder, :parentGroupID, :noOfLeafs, :conjoin, :oddMemberLeftOut, :groupIDs=>[], :leafIDs=>[], :sideIDs=>[]) + end + + def group_params_batch_update + params.permit(:groups => [:id, :attributes=>[:type, :title, :direction, :tacketed=>[], :sewing=>[]]]) + end + + def group_params_batch_delete + params.permit(:projectID, :groups => []) + end + +end diff --git a/viscoll-api/app/controllers/images_controller.rb b/viscoll-api/app/controllers/images_controller.rb new file mode 100644 index 00000000..c561df05 --- /dev/null +++ b/viscoll-api/app/controllers/images_controller.rb @@ -0,0 +1,225 @@ +class ImagesController < ApplicationController + before_action :authenticate!, except: [:show, :getZipImages] + + # POST /images + def uploadImages + begin + projectIDs = [] + if image_create_params.to_h.key?("projectID") + @project = Project.find(image_create_params.to_h[:projectID]) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + projectIDs.push(@project.id.to_s) + end + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "project not found with id #{params[:projectID]}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + newImages = [] + allImages = image_create_params.to_h[:images] + allImages.each do |image_data| + filename = image_data[:filename].parameterize.underscore + extension = image_data[:content].split("image/").last.split(";base64").first + imageIO = Shrine.data_uri(image_data[:content]) + uploader = Shrine.new(:store) + uploaded_file = uploader.upload(imageIO, metadata: {"filename"=>"#{filename}.#{extension}"}) + image = Image.new(user: current_user, filename: "#{filename}.#{extension}", fileID: uploaded_file.id, metadata: uploaded_file.metadata, projectIDs: projectIDs) + if image.valid? + image.save + else + copyCounter = 1 + while !image.save do + if image.errors.key?("filename") and image.errors[:filename][0].include?("Image with filename") + # Duplicate filename. Create Image with new filename+"_copy(copyCounter)" + filename = "#{image_data[:filename].parameterize.underscore}_copy(#{copyCounter})" + image = Image.new(user: current_user, filename: "#{filename}.#{extension}", fileID: uploaded_file.id, metadata: uploaded_file.metadata, projectIDs: projectIDs) + copyCounter += 1 + else + image.destroy + render json: image.errors, status: :unprocessable_entity and return + end + end + end + newImages.push(image) + end + @projects = current_user.projects + @images = newImages + render :'projects/index', status: :ok and return + end + + # GET /images/:imageID + def show + begin + # p params[:imageID_filename] + imageID = params[:imageID_filename].split("_", 2)[0] + filename = params[:imageID_filename].split("_", 2)[1] + @image = Image.find(imageID) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "image not found with id #{imageID}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + # Get image file + path = "#{Rails.root}/public/uploads/#{@image.fileID}" + File.open(path, 'rb') do |image| + send_file image, :type => @image.metadata['mime_type'], :disposition => 'inline' + end + end + + + # GET /images/zip/:imageID_projectID + def getZipImages + begin + projectID = params[:id] + zipFilePath = "#{Rails.root}/public/uploads/#{projectID}_images.zip" + send_file zipFilePath, :type => 'application/zip', :disposition => 'inline' + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + + # PUT/PATCH /images/link + def link + projectIDs = image_link_unlink_params.to_h[:projectIDs] + imageIDs = image_link_unlink_params.to_h[:imageIDs] + projects = [] + projectIDs.each do |projectID| + begin + project = Project.find(projectID) + if (project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + projects.push(project) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "project not found with id #{projectID}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + images = [] + imageIDs.each do |imageID| + begin + image = Image.find(imageID) + if (image.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + images.push(image) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "image not found with id #{imageID}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + projects.each do |project| + images.each do |image| + if not image.projectIDs.include? project.id.to_s + image.projectIDs.push(project.id.to_s) + image.save + end + end + end + @projects = current_user.projects + @images = current_user.images + render :'projects/index', status: :ok and return + end + + + # PUT/PATCH /images/unlink + def unlink + projectIDs = image_link_unlink_params.to_h[:projectIDs] + imageIDs = image_link_unlink_params.to_h[:imageIDs] + projects = [] + projectIDs.each do |projectID| + begin + project = Project.find(projectID) + if (project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + projects.push(project) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "project not found with id #{projectID}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + images = [] + imageIDs.each do |imageID| + begin + image = Image.find(imageID.split("_", 2)[0]) + if (image.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + images.push(image) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "image not found with id #{imageID.split("_", 2)[0]}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + projects.each do |project| + images.each do |image| + if image.projectIDs.include? project.id.to_s + image.projectIDs.delete(project.id.to_s) + # Unlink All Sides that belongs to this Project that has this Image mapped to it. + image.sideIDs.each do |sideID| + side = project.sides.where(:id => sideID).first + if side + side.image = {} + side.save + image.sideIDs.delete(sideID) + end + end + image.save + end + end + end + @projects = current_user.projects + @images = current_user.images + render :'projects/index', status: :ok and return + end + + + # DELETE /images + def destroy + images = [] + images_destroy_params.to_h[:imageIDs].each do |imageIDParam| + begin + imageID = imageIDParam.split("_", 2)[0] + image = Image.find(imageID) + images.push(image) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "image not found with id #{imageID}"}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + if (image.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + end + images.each do |image| + image.destroy + end + @projects = current_user.projects + @images = current_user.images + render :'projects/index', status: :ok and return + end + + + private + def image_create_params + params.permit(:projectID, :images => [:filename, :content]) + end + + def images_destroy_params + params.permit(:imageIDs => []) + end + + def image_link_unlink_params + params.permit(:projectIDs => [], :imageIDs => []) + end + +end diff --git a/viscoll-api/app/controllers/import_controller.rb b/viscoll-api/app/controllers/import_controller.rb new file mode 100644 index 00000000..9a7bdf35 --- /dev/null +++ b/viscoll-api/app/controllers/import_controller.rb @@ -0,0 +1,46 @@ +class ImportController < ApplicationController + before_action :authenticate! + + # PUT /projects/import + def index + errorMessage = "Sorry, the imported data cannot be validated. Please check your file for errors and make sure the correct import format is selected above." + importData = imported_data.to_h[:importData] + importFormat = imported_data.to_h[:importFormat] + imageData = imported_data.to_h[:imageData] + begin + case importFormat + when "json" + handleJSONImport(JSON.parse(importData)) + when "xml" + xml = Nokogiri::XML(importData) + schema = Nokogiri::XML::RelaxNG(File.open("public/viscoll-datamodel2.rng")) + schema2 = Nokogiri::XML::RelaxNG(File.open("public/viscoll-datamodel2.0.rng")) + errors = schema.validate(xml) + errors2 = schema2.validate(xml) + if errors.empty? || errors2.empty? + handleXMLImport(xml) + else + render json: {error: errors+errors2}, status: :unprocessable_entity and return + end + end + newProject = current_user.projects.order_by(:updated_at => 'desc').first + handleMappingImport(newProject, imageData, current_user) + current_user.reload + @projects = current_user.projects.order_by(:updated_at => 'desc') + @images = current_user.images + render :'projects/index', status: :ok and return + rescue Exception => e + render json: {error: errorMessage}, status: :unprocessable_entity and return + ensure + end + end + + + + private + # Never trust parameters from the scary Internet, only allow the white list through. + def imported_data + params.permit(:importData, :importFormat, :imageData) + end + +end diff --git a/viscoll-api/app/controllers/leafs_controller.rb b/viscoll-api/app/controllers/leafs_controller.rb new file mode 100644 index 00000000..ce1b4584 --- /dev/null +++ b/viscoll-api/app/controllers/leafs_controller.rb @@ -0,0 +1,294 @@ +class LeafsController < ApplicationController + before_action :authenticate! + before_action :set_leaf, only: [:update, :destroy] + + # POST /leafs + def create + memberOrder = additional_params.to_h[:memberOrder] + noOfLeafs = additional_params.to_h[:noOfLeafs] + conjoin = additional_params.to_h[:conjoin] + oddMemberLeftOut = additional_params.to_h[:oddMemberLeftOut] + leafIDs = additional_params.to_h[:leafIDs] + sideIDs = additional_params.to_h[:sideIDs] + project_id = leaf_params.to_h[:project_id] + parentID = leaf_params.to_h[:parentID] + + # Validation error for leaf_params + @leafErrors = validateLeafParams(project_id, parentID) + if @leafErrors[:project_id].length>0 || @leafErrors[:parentID].length>0 + render json: {leaf: @leafErrors}, status: :unprocessable_entity and return + end + + # Validation errors checking for additional parameters + @additionalErrors = validateAdditionalLeafParams(project_id, parentID, memberOrder, noOfLeafs, conjoin, oddMemberLeftOut) + hasAdditionalErrors = false + @additionalErrors.each_value do |value| + if value.length>0 + hasAdditionalErrors = true + end + end + if hasAdditionalErrors + render json: {additional: @additionalErrors}, status: :unprocessable_entity and return + end + + # Attempt to validate ownership + @project = Project.find(project_id) + if current_user.id != @project.user_id + render json: { leaf: { project_id: ['unauthorized project_id'] } }, status: :unauthorized and return + end + + # Skip all callbacks for side creation if leafIDs and SideIDs were give in the request + begin + if (leafIDs and sideIDs) + Leaf.skip_callback(:create, :before, :create_sides) + end + newlyAddedLeafIDs = [] + newlyAddedLeafs = [] + sideIDIndex = 0 + noOfLeafs.times do |leafIDIndex| + @leaf = Leaf.new(leaf_params) + if leafIDs + @leaf.id = leafIDs[leafIDIndex] + end + @leaf.nestLevel = @group.nestLevel + if @leaf.save + newlyAddedLeafs.push(@leaf) + newlyAddedLeafIDs.push(@leaf.id.to_s) + # Create new sides for this leaf with given SideIDs + if (leafIDs and sideIDs) + recto = Side.new({parentID: @leaf.id.to_s, project: @leaf.project, texture: "Hair", id: sideIDs[sideIDIndex]}) + verso = Side.new({parentID: @leaf.id.to_s, project: @leaf.project, texture: "Flesh", id: sideIDs[sideIDIndex+1] }) + recto.id = "Recto_"+recto.id.to_s + verso.id = "Verso_"+verso.id.to_s + recto.save + verso.save + @leaf.rectoID = recto.id + @leaf.versoID = verso.id + @leaf.save + end + else + render json: {leaf: @leaf.errors}, status: :unprocessable_entity and return + end + sideIDIndex += 2 + end + rescue + ensure + if (leafIDs and sideIDs) + Leaf.set_callback(:create, :before, :create_sides) + end + end + + # Time to Auto-Conjoin + autoConjoinLeaves(newlyAddedLeafs, oddMemberLeftOut) if conjoin + + # Add leaves to parent group + @group.add_members(newlyAddedLeafIDs, memberOrder) + + end + + + # PATCH/PUT /leafs/1 + def update + if (leaf_params.to_h.key?(:conjoined_to)) + # HANDLE SPECIAL CASE FOR conjoined_to + update_conjoined_partner(leaf_params.to_h[:conjoined_to]) + end + if @leaf.update(leaf_params) + if (leaf_params.to_h.key?(:attached_below)||leaf_params.to_h.key?(:attached_above)) + update_attached_to() + elsif leaf_params.to_h.key?(:material) and leaf_params.to_h[:material] == "Paper" + handle_paper_update(@leaf) + end + else + render json: {leaf: @leaf.errors}, status: :unprocessable_entity and return + end + end + + + # PATCH/PUT /leafs + def updateMultiple + begin + allLeafs = leaf_params_batch_update.to_h[:leafs] + begin + @project = Project.find(leaf_params_batch_update.to_h[:project_id]) + rescue Mongoid::Errors::DocumentNotFound => e + render json: {error: "project not found with id "+params[:project_id]}, status: :unprocessable_entity and return + end + allLeafs.each do |leaf_params, index| + begin + @leaf = Leaf.find(leaf_params[:id]) + rescue Exception => e + render json: {leafs: ["leaf not found with id "+leaf_params[:id]]}, status: :unprocessable_entity and return + end + if @leaf.project.user_id != current_user.id + render json: {error: ""}, status: :unauthorized and return + end + if !@leaf.update(leaf_params[:attributes]) + render json: {leafs: {attributes: {index: @leaf.errors}}}, status: :unprocessable_entity and return + end + if (leaf_params[:attributes].key?(:attached_below)||leaf_params[:attributes].key?(:attached_above)) + update_attached_to() + elsif leaf_params[:attributes].key?(:material) and leaf_params[:attributes][:material] == "Paper" + handle_paper_update(@leaf) + end + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + # DELETE /leafs/1 + def destroy + begin + parent = @project.groups.find(@leaf.parentID) + memberOrder = parent.memberIDs.index(@leaf.id.to_s) + # Detach its conjoined leaf + if @leaf.conjoined_to + @project.leafs.find(@leaf.conjoined_to).update(conjoined_to: nil) + end + if @leaf.attached_above != "None" + # Detach its above attached leaf + aboveLeaf = @project.leafs.find(parent.memberIDs[memberOrder-1]) + aboveLeaf.update(attached_below: "None") + end + if @leaf.attached_below != "None" + # Detach its below attached leaf + belowLeaf = @project.leafs.find(parent.memberIDs[memberOrder+1]) + belowLeaf.update(attached_above: "None") + end + @leaf.remove_from_group() + @leaf.destroy + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + # DELETE /leafs.json + def destroyMultiple + begin + allLeafs = leaf_params_batch_delete.to_h[:leafs] + project_id = Leaf.find(allLeafs[0]).project_id + @project = Project.find(project_id) + + parentAndChildren = {} + + allLeafs.each_with_index do |leafID| + leaf = Leaf.find(leafID) + if !@parent || @parent.id.to_s != leaf.parentID + @parent = @project.groups.find(leaf.parentID) + end + memberOrder = @parent.memberIDs.index(leaf.id.to_s) + if leaf.project.user_id != current_user.id + render json: {error: ""}, status: :unauthorized and return + end + + # Detach its conjoined leaf if any + if leaf.conjoined_to + @project.leafs.find(leaf.conjoined_to).update(conjoined_to: nil) + end + if leaf.attached_above != "None" + # Detach its above attached leaf + aboveLeaf = @project.leafs.find(@parent.memberIDs[memberOrder-1]) + aboveLeaf.update(attached_below: "None") + end + if leaf.attached_below != "None" + # Detach its below attached leaf + belowLeaf = @project.leafs.find(@parent.memberIDs[memberOrder+1]) + belowLeaf.update(attached_above: "None") + end + leaf.destroy + # Add leaf to list of leaves to detach from parent + if parentAndChildren[leaf.parentID] == nil + parentAndChildren[leaf.parentID] = [leaf.id.to_s] + else + parentAndChildren[leaf.parentID].push(leaf.id.to_s) + end + end + + # Detach all leaves from parent(s) + parentAndChildren.each do |parentID, leafIDs| + @project.groups.find(parentID).remove_members(leafIDs) + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + # CONJOIN /leafs.json + def conjoinLeafs + begin + leafIDs = leaf_params_batch_delete.to_h[:leafs] + leaves = [] + # VALIDATION ERRORS + @errors = [] + haveErrors = false + allowed_project_ids = current_user.projects.pluck(:id).collect { |pid| pid.to_s } + leafIDs.each do |leafID| + begin + leaf = Leaf.find(leafID) + if not allowed_project_ids.include?(leaf.project_id.to_s) + render json: {error: ""}, status: :unauthorized and return + end + leaves.push(leaf) + rescue Exception => e + @errors.push("leaf not found with id "+leafID) + haveErrors = true + end + end + if leafIDs.size < 2 + @errors.push("Minimum of 2 leaves required to conjoin") + haveErrors = true + end + if haveErrors + render json: {leafs: @errors}, status: :unprocessable_entity and return + end + @project = Project.find(leaves[0].project_id) + autoConjoinLeaves(leaves, (leaves.length+1)/2) + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + + + private + # Use callbacks to share common setup or constraints between actions. + def set_leaf + begin + @leaf = Leaf.find(params[:id]) + @project = Project.find(@leaf.project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "leaf not found with id "+params[:id]}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + # Never trust parameters from the scary internet, only allow the white list through. + def leaf_params + params.require(:leaf).permit(:id, :project_id, :parentID, :material, :type, :conjoined_to, :stub, :attached_above, :attached_below) + end + + def additional_params + params.require(:additional).permit(:memberOrder, :noOfLeafs, :conjoin, :oddMemberLeftOut, :leafIDs=>[], :sideIDs=>[]) + end + + def leaf_params_batch_update + params.permit(:project_id, :leafs => [:id, :attributes=>[:conjoined_to, :type, :material, :stub, :attached_above, :attached_below]]) + end + + def leaf_params_batch_delete + params.permit(:leafs => []) + end + + def leaf_params_conjoin + params.permit(:leafs => []) + end + +end diff --git a/viscoll-api/app/controllers/notes_controller.rb b/viscoll-api/app/controllers/notes_controller.rb new file mode 100644 index 00000000..2b2c7ed7 --- /dev/null +++ b/viscoll-api/app/controllers/notes_controller.rb @@ -0,0 +1,216 @@ +class NotesController < ApplicationController + before_action :authenticate! + before_action :set_note, only: [:update, :link, :unlink, :destroy] + before_action :set_attached_project, only: [:createType, :deleteType, :updateType] + + # POST /notes + def create + @note = Note.new(note_create_params) + begin + @project = Project.find(@note.project_id) + rescue Mongoid::Errors::DocumentNotFound + render json: {project_id: "project not found with id "+@note.project_id}, status: :unprocessable_entity and return + end + if @project.user != current_user + render json: {error: ''}, status: :unauthorized and return + end + if @note.save + if not Project.find(@note.project_id).noteTypes.include?(@note.type) + @note.delete + render json: {type: "should be one of " +Project.find(@note.project_id).noteTypes.to_s}, status: :unprocessable_entity and return + end + else + render json: @note.errors, status: :unprocessable_entity and return + end + end + + # PATCH/PUT /notes/1 + def update + type = note_update_params.to_h[:type] + if not Project.find(@note.project_id).noteTypes.include?(type) + render json: {type: "should be one of " +Project.find(@note.project_id).noteTypes.to_s}, status: :unprocessable_entity and return + end + if !@note.update(note_update_params) + render json: @note.errors, status: :unprocessable_entity and return + end + end + + # DELETE /notes/1 + def destroy + @note.destroy + end + + # PUT /notes/1/link + def link + begin + objects = note_object_link_params.to_h[:objects] + objects.each do |object| + type = object[:type] + id = object[:id] + begin + case type + when "Group" + @object = Group.find(id) + authorized = @object.project.user_id == current_user.id + when "Leaf" + @object = Leaf.find(id) + authorized = @object.project.user_id == current_user.id + when "Recto", "Verso" + @object = Side.find(id) + authorized = @object.project.user_id == current_user.id + else + render json: {type: "object not found with type "+type}, status: :unprocessable_entity and return + end + unless authorized + render json: {error: ''}, status: :unauthorized and return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {id: type + " object not found with id "+id}, status: :unprocessable_entity and return + end + @object.notes.push(@note) + @object.save + if (not @note.objects[type].include?(id)) + @note.objects[type].push(id) + end + @note.save + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + # PUT /notes/1/unlink + def unlink + begin + objects = note_object_link_params.to_h[:objects] + objects.each do |object| + type = object[:type] + id = object[:id] + begin + case type + when "Group" + @object = Group.find(id) + authorized = @object.project.user_id == current_user.id + when "Leaf" + @object = Leaf.find(id) + authorized = @object.project.user_id == current_user.id + when "Recto", "Verso" + @object = Side.find(id) + authorized = @object.project.user_id == current_user.id + else + render json: {type: "object not found with type "+type}, status: :unprocessable_entity and return + end + unless authorized + render json: {error: ''}, status: :unauthorized and return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {id: type + " object not found with id "+id}, status: :unprocessable_entity and return + end + @object.notes.delete(@note) + @object.save + @note.objects[type].delete(id) + @note.save + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + + # POST /notes/type + def createType + type = note_type_params.to_h[:type] + if @project.noteTypes.include?(type) + render json: {type: type+" type already exists in the project"}, status: :unprocessable_entity and return + else + @project.noteTypes.push(type) + @project.save + end + end + + + # DELETE /notes/type + def deleteType + type = note_type_params.to_h[:type] + if not @project.noteTypes.include?(type) + render json: {type: type+" type doesn't exist in the project"}, status: :unprocessable_entity and return + else + @project.noteTypes.delete(type) + @project.save + @project.notes.where(type: type).each do |note| + note.update(type: "Unknown") + note.save + end + end + end + + + # PUT /notes/type + def updateType + old_type = note_type_params.to_h[:old_type] + type = note_type_params.to_h[:type] + if not @project.noteTypes.include?(old_type) + render json: {old_type: old_type+" type doesn't exist in the project"}, status: :unprocessable_entity and return + elsif @project.noteTypes.include?(type) + render json: {type: type+" already exists in the project"}, status: :unprocessable_entity and return + else + indexToEdit = @project.noteTypes.index(old_type) + @project.noteTypes[indexToEdit] = type + @project.save + @project.notes.where(type: old_type).each do |note| + note.update(type: type) + note.save + end + end + end + + + + private + # Use callbacks to share common setup or constraints between actions. + def set_note + begin + @note = Note.find(params[:id]) + @project = Project.find(@note.project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "note not found with id "+params[:id]}, status: :not_found and return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + def set_attached_project + project_id = note_type_params.to_h[:project_id] + begin + @project = Project.find(project_id) + if @project.user_id != current_user.id + render json: {error: ""}, status: :unauthorized and return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity and return + end + end + + # Never trust parameters from the scary internet, only allow the white list through. + def note_create_params + params.require(:note).permit(:project_id, :id, :title, :type, :description, :show) + end + + def note_update_params + params.require(:note).permit(:title, :type, :description, :show) + end + + def note_object_link_params + params.permit(:objects => [:id, :type]) + end + + def note_type_params + params.require(:noteType).permit(:type, :project_id, :old_type) + end + + +end diff --git a/viscoll-api/app/controllers/projects_controller.rb b/viscoll-api/app/controllers/projects_controller.rb new file mode 100644 index 00000000..6f2cfeb1 --- /dev/null +++ b/viscoll-api/app/controllers/projects_controller.rb @@ -0,0 +1,225 @@ +class ProjectsController < ApplicationController + before_action :authenticate!, except: [:viewOnly] + before_action :set_project, only: [:show, :update, :destroy, :createManifest, :updateManifest, :deleteManifest, :clone] + + + # GET /projects + def index + @projects = current_user.projects + @images = current_user.images + end + + # GET /projects/1 + def show + @data = generateResponse() + @projects = current_user.projects + @images = current_user.images + end + + # GET /projects/1/viewOnly + def viewOnly + @project = Project.find(params[:id]) + @data = generateResponse() + render json: @data, status: :ok and return + end + + # POST /projects + def create + begin + # Run validatins for groups params + allGroups = group_params.to_h["groups"] + folioNumber = group_params.to_h["folioNumber"] + pageNumber = group_params.to_h["pageNumber"] + startingTexture = group_params.to_h["startingTexture"] + + validationResult = validateProjectCreateGroupsParams(allGroups) + if (not validationResult[:status]) + render json: {groups: validationResult[:errors]}, status: :unprocessable_entity and return + end + # Instantiate a new project with the given params + @project = Project.new(project_params) + # If the project contains noteTypes, add the 'Unknown' type if its not present + if (not @project.noteTypes.empty? and not @project.noteTypes.include?('Unknown')) + @project.noteTypes.push('Unknown') + end + # Associate the current logged_in user to this project + @project.user = current_user + if @project.save + # If groups params were given, create the Groups & Leaves & auto-conjoin if required + if (not allGroups.empty?) + addGroupsLeafsConjoin(@project, allGroups, folioNumber, pageNumber, startingTexture) + end + # Get list of all projects of current user to return in response + @projects = current_user.projects.order_by(:updated_at => 'desc') + @images = current_user.images + render :index, status: :ok and return + else + render json: {project: @project.errors}, status: :unprocessable_entity and return + end + rescue Exception => e + render json: {errors: e.message}, status: :bad_request and return + end + end + + # PATCH/PUT /projects/1 + def update + begin + @project = Project.find(params[:id]) + if @project.update(project_params) + @projects = current_user.projects + @images = current_user.images + render :index, status: :ok and return + else + render json: {project: @project.errors}, status: :unprocessable_entity and return + end + rescue Exception => e + render json: {errors: e.message}, status: :bad_request and return + end + end + + # DELETE /projects/1 + def destroy + deleteUnlinkedImages = project_delete_params.to_h["deleteUnlinkedImages"] + begin + # Skip some callbacks + Leaf.skip_callback(:destroy, :before, :unlink_notes) + if deleteUnlinkedImages + Image.skip_callback(:destroy, :before, :unlink_sides_before_delete) + current_user.images.where({ "projectIDs" => { '$eq': [@project.id.to_s] } }).each do | image | + image.destroy + end + end + @project.destroy + @projects = current_user.projects + @images = current_user.images + render :index, status: :ok and return + rescue Exception => e + render json: {errors: e.message}, status: :bad_request and return + ensure + # Enable callbacks again + Image.set_callback(:destroy, :before, :unlink_sides_before_delete) + Leaf.set_callback(:destroy, :before, :unlink_notes) + end + end + + # POST /projects/:id/manifests + def createManifest + begin + manifest = manifest_params.to_h + if not manifest.key?("id") + manifestID = Project.new.id.to_s + else + manifestID = manifest[:id] + end + @project.manifests[manifestID] = {id: manifestID, url: manifest[:url]} + @project.save + @data = generateResponse() + @projects = current_user.projects + @images = current_user.images + render :show, status: :ok and return + rescue Exception => e + render json: {errors: e}, status: :bad_request and return + end + end + + # PATCH/PUT /projects/:id/manifests + def updateManifest + begin + manifest = manifest_params.to_h + if not manifest.key?("id") + render json: {error: "Param required: id."}, status: :unprocessable_entity and return + end + if not @project.manifests.key?(manifest["id"]) + render json: {error: "Manifest with id: " + manifest["id"] + " not found in project with id: " + @project.id.to_s + "."}, status: :unprocessable_entity and return + end + # ONLY UPDATING MANIFEST NAME FOR NOW + @project.manifests[manifest["id"]]["name"] = manifest["name"] + @project.save + rescue Exception => e + render json: {errors: e.message}, status: :bad_request and return + end + end + + # DELETE /projects/:id/manifests + def deleteManifest + begin + manifestIDToDelete = manifest_params.to_h[:id] + if not @project.manifests.key?(manifestIDToDelete) + render json: {error: "Manifest with id: " + manifestIDToDelete + " not found in project with id: " + @project.id.to_s + "."}, status: :unprocessable_entity and return + end + @project.manifests.delete(manifestIDToDelete) + # Update all sides that have the deleted manuscripts mapping + @project.sides.each do |side| + if side[:image][:manifestID] == manifestIDToDelete + side.update(image: {}) + end + end + @project.save + rescue Exception => e + render json: {errors: e.message}, status: :bad_request and return + end + end + + + # GET /projects/:id/clone + def clone + begin + exportedData = buildJSON(@project) + export = { + project: exportedData[:project], + Groups: exportedData[:groups], + Leafs: exportedData[:leafs], + Rectos: exportedData[:rectos], + Versos: exportedData[:versos], + Notes: exportedData[:notes], + } + handleJSONImport(JSON.parse(export.to_json)) + newProject = current_user.projects.order_by(:updated_at => 'desc').first + newProject.sides.each do |side| + if !side.image.empty? and side.image["manifestID"]=="DIYImages" + filename = side.image["label"] + image = current_user.images.where(:filename => filename).first + !(image.sideIDs.include?(side.id.to_s)) ? image.sideIDs.push(side.id.to_s) : nil + !(image.projectIDs.include?(newProject.id.to_s)) ? image.projectIDs.push(newProject.id.to_s) : nil + image.save + end + end + @projects = current_user.projects.order_by(:updated_at => 'desc') + @images = current_user.images + render :index, status: :ok and return + rescue Exception => e + p e.message + end + end + + + private + def set_project + begin + @project = Project.find(params[:id]) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + rescue Exception => e + render json: {error: "project not found with id "+params[:id]}, status: :not_found and return + end + end + + # Never trust parameters from the scary Internet, only allow the white list through. + def project_params + params.require(:project).permit(:title, :shelfmark, :metadata=>{}, :noteTypes=>[], :preferences=>{}) + end + + def project_delete_params + params.permit(:deleteUnlinkedImages) + end + + def group_params + params.permit(:folioNumber, :pageNumber, :startingTexture, :groups => [:number, :leaves, :direction, :conjoin, :oddLeaf]) + end + + def manifest_params + params.require(:manifest).permit(:id, :name, :url) + end + +end diff --git a/viscoll-api/app/controllers/registrations_controller.rb b/viscoll-api/app/controllers/registrations_controller.rb new file mode 100644 index 00000000..a0f53c9e --- /dev/null +++ b/viscoll-api/app/controllers/registrations_controller.rb @@ -0,0 +1,19 @@ +class RegistrationsController < RailsJwtAuth::RegistrationsController + + def create + begin + user = RailsJwtAuth.model.new(registration_create_params) + user.save ? render_registration(user) : render_422(user.errors) + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + + end + + private + def registration_create_params + params.require(RailsJwtAuth.model_name.underscore).permit( + RailsJwtAuth.auth_field_name, :password, :password_confirmation, :name + ) + end +end diff --git a/viscoll-api/app/controllers/sessions_controller.rb b/viscoll-api/app/controllers/sessions_controller.rb new file mode 100644 index 00000000..3ddfc68a --- /dev/null +++ b/viscoll-api/app/controllers/sessions_controller.rb @@ -0,0 +1,71 @@ +class SessionsController < RailsJwtAuth::SessionsController + + def create + user = RailsJwtAuth.model.where(RailsJwtAuth.auth_field_name => + session_create_params[RailsJwtAuth.auth_field_name].to_s.downcase).first + + if !user + render_422 session: [create_session_error] + elsif user.respond_to?('confirmed?') && !user.confirmed? + render_422 session: [I18n.t('rails_jwt_auth.errors.unconfirmed')] + elsif user.authenticate(session_create_params[:password]) + @userProjects = [] + begin + @userProjects = user.projects + rescue + end + @userToken = get_jwt(user) + @user = user + render :index, status: :ok, location: { + userProjects: @userProjects, + userToken: @userToken, + user: @user + } + else + render_422 session: [create_session_error] + end + end + + def destroy + begin + authenticateDestroy! + current_user.destroy_auth_token Jwt::Request.new(request).auth_token + render_204 + rescue Exception => e + render json: {error: "Authorization Header: "+e.message}, status: :unprocessable_entity + end + end +end + + + +module Jwt + class Request + def initialize(request) + return unless request.env['HTTP_AUTHORIZATION'] + @jwt = request.env['HTTP_AUTHORIZATION'].split.last + + begin + @jwt_info = RailsJwtAuth::Jwt::Manager.decode(@jwt) + rescue JWT::ExpiredSignature, JWT::VerificationError + @jwt_info = false + end + end + + def valid? + @jwt && @jwt_info && RailsJwtAuth::Jwt::Manager.valid_payload?(payload) + end + + def payload + @jwt_info ? @jwt_info[0] : nil + end + + def header + @jwt_info ? @jwt_info[1] : nil + end + + def auth_token + payload ? payload['auth_token'] : nil + end + end +end diff --git a/viscoll-api/app/controllers/sides_controller.rb b/viscoll-api/app/controllers/sides_controller.rb new file mode 100644 index 00000000..a2c63e95 --- /dev/null +++ b/viscoll-api/app/controllers/sides_controller.rb @@ -0,0 +1,155 @@ +class SidesController < ApplicationController + before_action :authenticate! + before_action :set_side, only: [:update] + + # PATCH/PUT /sides/1 + def update + begin + if !@side.update(side_params) + render json: @side.errors, status: :unprocessable_entity and return + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + # PATCH/PUT /sides + def updateMultiple + begin + allSides = side_params_batch_update.to_h[:sides] + # VALIDATIONS + @errors = [] + haveErrors = false + sides = [] + allSides.each do |sideID| + begin + side = Side.find(sideID) + sides.push(side) + rescue Exception => e + @errors.push("side not found with id "+sideID) + haveErrors = true + end + end + @project = Project.find(sides[0].project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + if haveErrors + render json: {sides: @errors}, status: :unprocessable_entity and return + end + allSides.each_with_index do |side_params, index| + side = sides[index] + previousSideImage = side.image.clone + if !side.update(side_params[:attributes]) + render json: side.errors, status: :unprocessable_entity and return + else + # SPEICAL CASE FOR DIY IMAGE MAPPING + if side_params[:attributes]["image"] + newSideImage = side_params[:attributes]["image"].clone + # If an image was linked, check if it was a DIY and link this Side to that Image + if newSideImage and !(newSideImage.empty?) and newSideImage["manifestID"]=="DIYImages" + imageID = newSideImage["url"].split("/")[-1].split("_", 2)[0] + begin + imageLinked = Image.find(imageID) + !(imageLinked.sideIDs.include?(side.id.to_s)) ? imageLinked.sideIDs.push(side.id.to_s) : nil + imageLinked.save + rescue Exception => e + end + end + # If an image was linked, check if this side was previously linked to a DIY Image and unlink this Side to that Image + if newSideImage and !newSideImage.empty? and !previousSideImage.empty? and previousSideImage["manifestID"]=="DIYImages" + imageID = previousSideImage["url"].split("/")[-1].split("_", 2)[0] + begin + imageUnlinked = Image.find(imageID) + if !imageLinked or imageLinked.id.to_s != imageUnlinked.id.to_s + imageUnlinked.sideIDs.include?(side.id.to_s) ? imageUnlinked.sideIDs.delete(side.id.to_s) : nil + imageUnlinked.save + end + rescue Exception => e + end + end + # If an Image was unlinked, check if it was a DIY and unlink this Side from the Image + if newSideImage and newSideImage.empty? and !previousSideImage.empty? and previousSideImage["manifestID"]=="DIYImages" + imageID = previousSideImage["url"].split("/")[-1].split("_", 2)[0] + begin + image = Image.find(imageID) + image.sideIDs.include?(side.id.to_s) ? image.sideIDs.delete(side.id.to_s) : nil + image.save + rescue Exception => e + end + end + end + end + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity and return + end + end + + + # PUT /sides/generateFolio + def generateFolio + folioNumberCount = side_params_generate.to_h[:startNumber].to_i + rectoIDs = side_params_generate.to_h[:rectoIDs] + versoIDs = side_params_generate.to_h[:versoIDs] + rectoIDs.each_with_index do | rectoID, index | + recto = Side.find(rectoID) + verso = Side.find(versoIDs[index]) + recto.update_attribute(:folio_number, folioNumberCount.to_s+"R") + verso.update_attribute(:folio_number, folioNumberCount.to_s+"V") + folioNumberCount += 1 + if index==0 + @project = Project.find(recto.project_id) + end + end + end + + + # PUT /sides/generatePageNumber + def generatePageNumber + pageNumberCount = side_params_generate.to_h[:startNumber].to_i + rectoIDs = side_params_generate.to_h[:rectoIDs] + versoIDs = side_params_generate.to_h[:versoIDs] + rectoIDs.each_with_index do | rectoID, index | + recto = Side.find(rectoID) + verso = Side.find(versoIDs[index]) + recto.update_attribute(:page_number, pageNumberCount.to_s) + pageNumberCount += 1 + verso.update_attribute(:page_number, pageNumberCount.to_s) + pageNumberCount += 1 + if index==0 + @project = Project.find(recto.project_id) + end + end + end + + + private + # Use callbacks to share common setup or constraints between actions. + def set_side + begin + @side = Side.find(params[:id]) + @project = Project.find(@side.project_id) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized and return + end + rescue Exception => e + render json: {error: "side not found"}, status: :not_found and return + end + end + + # Never trust parameters from the scary internet, only allow the white list through. + def side_params + params.require(:side).permit(:folio_number, :page_number, :texture, :script_direction, :image=>[:manifestID, :label, :url]) + end + + def side_params_batch_update + params.permit(:sides => [:id, :attributes=>[:folio_number, :page_number, :texture, :script_direction, :image=>[:manifestID, :label, :url]]]) + end + + def side_params_generate + params.permit(:startNumber, :rectoIDs => [], :versoIDs => []) + end + +end diff --git a/viscoll-api/app/controllers/users_controller.rb b/viscoll-api/app/controllers/users_controller.rb new file mode 100644 index 00000000..dd7b6633 --- /dev/null +++ b/viscoll-api/app/controllers/users_controller.rb @@ -0,0 +1,52 @@ +class UsersController < ApplicationController + before_action :authenticate! + before_action :set_user, only: [:show, :update, :destroy] + + # GET /users/1 + def show + end + + # PATCH/PUT /users/1 + def update + if user_params_with_password[:password] != nil + action = current_user.update_with_password(user_params_with_password) + else + action = current_user.update_attributes(user_params) + end + if action + @user = User.find(params[:id]) + render :show, status: :ok and return + else + render json: current_user.errors, status: :unprocessable_entity and return + end + + end + + # DELETE /users/1 + def destroy + @user.destroy + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_user + begin + @user = User.find(params[:id]) + if (@user!=current_user) + render json: {error: ""}, status: :unauthorized and return + end + rescue Exception => e + render json: {error: "user not found with id "+params[:id]}, status: :not_found and return + end + end + + # Only allow a trusted parameter "white list" through. + def user_params + params.require(:user).permit(:email, :name) + end + + # Only allow a trusted parameter "white list" through. + def user_params_with_password + params.require(:user).permit(:email, :name, :current_password, :password) + end +end diff --git a/viscoll-api/app/helpers/controller_helper/export_helper.rb b/viscoll-api/app/helpers/controller_helper/export_helper.rb new file mode 100644 index 00000000..45b99e67 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/export_helper.rb @@ -0,0 +1,765 @@ +module ControllerHelper + module ExportHelper + + def buildJSON(project) + @project.reload + @projectInformation = {} + @groupIDs = @project.groupIDs + @leafIDs = [] + @rectoIDs = [] + @versoIDs = [] + @groups = {} + @leafs = {} + @rectos = {} + @versos = {} + @notes = {} + + @projectInformation = { + "title": @project.title, + "shelfmark": @project.shelfmark, + "metadata": @project.metadata, + "preferences": @project.preferences, + "manifests": @project.manifests, + "noteTypes": @project.noteTypes + } + + rootMemberOrder = 1 + @groupIDs.each_with_index do | groupID, index| + group = @project.groups.find(groupID) + @groups[index + 1] = { + "params": { + "type": group.type, + "title": group.title, + "direction": group.direction, + "nestLevel": group.nestLevel + }, + "tacketed": group.tacketed, + "sewing": group.sewing, + "parentOrder": group.parentID, + "memberOrders": group.memberIDs + } + if group.nestLevel == 1 + rootMemberOrder += 1 + end + end + + # Generate @leafIDs list + @groups.each do | groupOrder, group | + if group[:params][:nestLevel] == 1 + getLeafMemberOrders(group[:memberOrders]) + end + end + + @leafIDs.each_with_index do | leafID, index | + leaf = @project.leafs.find(leafID) + @leafs[index + 1] = { + "params": { + "material": leaf.material, + "type": leaf.type, + "attached_above": leaf.attached_above, + "attached_below": leaf.attached_below, + "stub": leaf.stub, + "nestLevel": leaf.nestLevel + }, + "conjoined_leaf_order": leaf.conjoined_to ? @leafIDs.index(leaf.conjoined_to) + 1 : nil, + "parentOrder": @groupIDs.index(leaf.parentID)+1, + "rectoOrder": index + 1, + "versoOrder": index + 1, + } + @rectoIDs.push(leaf.rectoID) + @versoIDs.push(leaf.versoID) + end + + # Transform group's members to global orders + # Transform group's tacketed and sewing to leaf global orders + # Transform group's parentID to group global order + @groups.each do | groupID, group | + memberOrders = [] + group[:memberOrders].each do |memberID| + if memberID[0] == "G" + memberOrders.push("Group_" + (@groupIDs.index(memberID)+1).to_s) + else + memberOrders.push("Leaf_" + (@leafIDs.index(memberID)+1).to_s) + end + end + group[:memberOrders] = memberOrders + tacketedLeafOrders, sewingLeafOrders = [], [] + group[:tacketed].each do |leafID| tacketedLeafOrders.push(@leafIDs.index(leafID)+1) end + group[:sewing].each do |leafID| sewingLeafOrders.push(@leafIDs.index(leafID)+1) end + group[:tacketed], group[:sewing] = tacketedLeafOrders, sewingLeafOrders + group[:parentOrder] = group[:parentOrder] ? @groupIDs.index(group[:parentOrder]) + 1 : nil + end + + @rectoIDs.each_with_index do | rectoID, index | + recto = @project.sides.find(rectoID) + parentOrder = @leafIDs.index(recto.parentID) + 1 + @rectos[index + 1] = { + "params": { + "folio_number": recto.folio_number ? recto.folio_number : "", + "page_number": recto.page_number ? recto.page_number : "", + "texture": recto.texture, + "image": recto.image, + "script_direction": recto.script_direction + }, + "parentOrder": parentOrder + } + end + + @versoIDs.each_with_index do | versoID, index | + verso = @project.sides.find(versoID) + parentOrder = @leafIDs.index(verso.parentID) + 1 + @versos[index + 1] = { + "params": { + "folio_number": verso.folio_number ? verso.folio_number : "", + "page_number": verso.page_number ? verso.page_number : "", + "texture": verso.texture, + "image": verso.image, + "script_direction": verso.script_direction + }, + "parentOrder": parentOrder + } + end + + @project.notes.each_with_index do | note, index | + @notes[index + 1] = { + "params": { + "title": note.title, + "type": note.type, + "description": note.description, + "show": note.show + }, + "objects": {} + } + @notes[index + 1][:objects][:Group] = note.objects["Group"].map { |groupID| @groupIDs.index(groupID)+1 } + @notes[index + 1][:objects][:Leaf] = note.objects["Leaf"].map { |leafID| @leafIDs.index(leafID)+1 } + @notes[index + 1][:objects][:Recto] = note.objects["Recto"].map { |rectoID| @rectoIDs.index(rectoID)+1 } + @notes[index + 1][:objects][:Verso] = note.objects["Verso"].map { |versoID| @versoIDs.index(versoID)+1 } + end + + return { + "project": @projectInformation, + "groups": @groups, + "leafs": @leafs, + "rectos": @rectos, + "versos": @versos, + "notes": @notes, + } + end + + + # Populate leaf orders recursively + def getLeafMemberOrders(memberIDs) + memberIDs.each_with_index do | memberID, index | + if memberID[0] == "G" + getLeafMemberOrders(@groups[@groupIDs.index(memberID)+1][:memberOrders]) + elsif memberID[0] == "L" + @leafIDs.push(memberID) + end + end + end + + + + + + def buildDotModel(project) + @groupIDs = project.groupIDs + @groups = {} + @leafIDs = [] + @leafs = {} + @rectos = {} + @versos = {} + @notes = {} + @noteTitles = [] + @allGroupAttributeValues = [] + @allLeafAttributeValues = [] + @allSideAttributeValues = [] + @groupIDs.each_with_index do |groupID, index| + if @groups.key?(groupID) + memberOrder = @groups[groupID][:memberOrder] + @groups[groupID] = project.groups.find(groupID) + @groups[groupID][:memberOrder] = memberOrder + else + @groups[groupID] = project.groups.find(groupID) + @groups[groupID][:memberOrder] = index+1 + end + if @groups[groupID][:memberIDs] + populateLeafSideObjects(@groups[groupID][:memberIDs], project) + end + end + + return Nokogiri::XML::Builder.new { |xml| + xml.viscoll :xmlns => "http://schoenberginstitute.org/schema/collation" do + idPrefix = project.shelfmark.parameterize.underscore + + # Project Attributes Taxonomy + ['preferences'].each do |attribute| + manuscriptAttribute = {"xml:id": 'manuscript_'+attribute} + xml.taxonomy manuscriptAttribute do + xml.label do + xml.text 'Manuscript ' + attribute + end + project[attribute].each do |key, value| + termID = {"xml:id": "manuscript_"+attribute+"_"+idPrefix+"_"+key} + xml.term termID do + xml.text value + end + end + end + end + if not project.manifests.empty? + manifestAttribute = {"xml:id": 'manifests'} + xml.taxonomy manifestAttribute do + xml.label do + xml.text 'List of Manifests' + end + project.manifests.each do |manifestID, manifest| + termID = {"xml:id": 'manifest_'+manifest["id"]} + xml.term termID do + xml.text manifest["url"] + end + end + end + end + + # Group Attributes Taxonomy + ['title', 'type', 'direction'].each do |attribute| + groupAttribute = {"xml:id": 'group_'+attribute} + xml.taxonomy groupAttribute do + xml.label do + xml.text 'List of values for Group ' + attribute + end + groupAttributeValues = [] + @groupIDs.each do |groupID| + group = @groups[groupID] + if not groupAttributeValues.include? group[attribute] + groupAttributeValues.push(group[attribute]) + end + end + groupAttributeValues.each do |attributeValue| + termID = {"xml:id": "group_"+attribute+"_"+attributeValue.parameterize.underscore} + xml.term termID do + xml.text attributeValue + end + end + @allGroupAttributeValues = @allGroupAttributeValues + groupAttributeValues + end + end + ['tacketed', 'sewing'].each do |attribute| + groupAttribute = {"xml:id": 'group_'+attribute} + groupAttributeValues = [] + @groupIDs.each do |groupID| + group = @groups[groupID] + leaves = "" + if not groupAttributeValues.include? group[attribute] + group[attribute].each do |leafID| + parents = parentsOrders(leafID, project) + leafemberOrder = parents.pop + idPostfix = parents.join("-")+"-"+leafemberOrder.to_s + leaves = leaves + " #" + idPrefix+"-"+idPostfix + " " + leaves = leaves.strip + end + end + if leaves != "" + xml.taxonomy groupAttribute do + xml.label do + xml.text 'List of Groups ' + attribute + end + parents = parentsOrders(groupID, project) + groupOrder = parents.pop + groupMemberOrder = group["memberOrder"] + idPostfix = parents.empty? ? groupOrder.to_s : parents.join("-")+"-"+groupOrder.to_s + termID = {"xml:id": "group_"+attribute+"_"+idPrefix+"-q-"+idPostfix} + xml.term termID do + xml.text leaves + end + end + @allGroupAttributeValues = @allGroupAttributeValues + [leaves] + end + end + end + # Member IDs of each Group + groupAttribute = {"xml:id": 'group_members'} + xml.taxonomy groupAttribute do + xml.label do + xml.text 'List of values for each Group\'s members' + end + @groupIDs.each do |groupID| + group = @groups[groupID] + memberIDs = [] + group.memberIDs.each do |memberID| + parents = parentsOrders(memberID, project) + memberOrder = parents.pop + if memberID[0]=="G" + idPostfix = parents.empty? ? memberOrder.to_s : parents.join("-")+"-"+memberOrder.to_s + memberIDs.push(idPrefix+"-q-"+idPostfix) + else + idPostfix = parents.join("-")+"-"+memberOrder.to_s + memberIDs.push(idPrefix+"-"+idPostfix) + end + + end + memberIDs = memberIDs.join(" #").strip + parents = parentsOrders(groupID, project) + groupOrder = parents.pop + groupMemberOrder = group["memberOrder"] + idPostfix = parents.empty? ? groupOrder.to_s : parents.join("-")+"-"+groupOrder.to_s + termID = {"xml:id": "group_members_"+idPrefix+"-q-"+idPostfix} + xml.term termID do + xml.text "#"+memberIDs + end + end + end + + # Leaf Attributes Taxonomy + ['material'].each do |attribute| + leafAttribute = {"xml:id": 'leaf_'+attribute} + leafAttributeValues = [] + @leafIDs.each do |leafID| + leaf = @leafs[leafID] + if not leafAttributeValues.include? leaf[attribute] and leaf[attribute] != "None" + leafAttributeValues.push(leaf[attribute]) + end + end + if not leafAttributeValues.empty? + xml.taxonomy leafAttribute do + xml.label do + xml.text 'List of values for Leaf ' + attribute + end + leafAttributeValues.each do |attributeValue| + termID = {"xml:id": "leaf_"+attribute+"_"+attributeValue.parameterize.underscore} + xml.term termID do + xml.text attributeValue + end + end + @allLeafAttributeValues += leafAttributeValues + end + end + end + leafAttribute = {"xml:id": 'leaf_attachment_method'} + xml.taxonomy leafAttribute do + xml.label do + xml.text 'List of Attachment Methods' + end + ['Glued_Above_Partial', 'Glued_Above_Complete', 'Glued_Above_Drumming', 'Glued_Above_Other'].each do |attribute| + termID = {"xml:id": attribute} + xml.term termID do + xml.text attribute.split("_")[0]+" ("+attribute.split("_")[2]+")" + end + end + ['Glued_Below_Partial', 'Glued_Below_Complete', 'Glued_Below_Drumming', 'Glued_Below_Other'].each do |attribute| + termID = {"xml:id": attribute} + xml.term termID do + xml.text attribute.split("_")[0]+" ("+attribute.split("_")[2]+")" + end + end + end + + # Side Attributes Taxonomy + ['texture', 'script_direction', 'page_number'].each do |attribute| + sideAttribute = {"xml:id": 'side_'+attribute} + sideAttributeValues = [] + @rectos.each do |rectoID, recto| + if recto[attribute] == nil and not sideAttributeValues.include? "EMPTY" + sideAttributeValues.push("EMPTY") + elsif recto[attribute] != nil and not sideAttributeValues.include? recto[attribute] and recto[attribute] != "None" + sideAttributeValues.push(recto[attribute]) + end + end + @versos.each do |versoID, verso| + if verso[attribute] == nil and not sideAttributeValues.include? "EMPTY" + sideAttributeValues.push("EMPTY") + elsif verso[attribute] != nil and not sideAttributeValues.include? verso[attribute] and verso[attribute] != "None" + sideAttributeValues.push(verso[attribute]) + end + end + if not sideAttributeValues.empty? + xml.taxonomy sideAttribute do + xml.label do + xml.text 'List of values for Side ' + attribute + end + sideAttributeValues.each do |attributeValue| + if attributeValue + termID = {"xml:id": "side_"+attribute+"_"+attributeValue.parameterize.underscore} + xml.term termID do + xml.text attributeValue + end + end + end + @allSideAttributeValues += sideAttributeValues + end + end + end + + # Note Attributes Taxonomy + if not project.notes.empty? + noteTitle = {"xml:id": 'note_title'} + xml.taxonomy noteTitle do + xml.label do + xml.text 'List of values for Note Titles' + end + project.notes.each_with_index do |note, index| + if not @noteTitles.include? note.title + @noteTitles.push(note.title) + end + end + @noteTitles.each do |noteTitle| + termID = {"xml:id": "note_title"+"_"+noteTitle.parameterize.underscore} + xml.term termID do + xml.text noteTitle + end + end + end + noteShow = {"xml:id": 'note_show'} + xml.taxonomy noteShow do + xml.label do + xml.text 'Whether to show Note in Visualizations' + end + termID = {"xml:id": "note_show"} + xml.term termID do + xml.text true + end + end + end + + + # STRUCTURE + xml.manuscript do + xml.title project.title + xml.shelfmark project.shelfmark + xml.date project.metadata[:date] + xml.direction :val => "l-r" + idPrefix = project.shelfmark.parameterize.underscore + xml.quires do + @groupIDs.each_with_index do |groupID, index| + group = @groups[groupID] + parents = parentsOrders(groupID, project) + groupOrder = parents.pop + groupMemberOrder = group["memberOrder"] + idPostfix = parents.empty? ? groupOrder.to_s : parents.join("-")+"-"+groupOrder.to_s + quireAttributes = {} + quireAttributes["xml:id"] = idPrefix+"-q-"+idPostfix + quireAttributes[:n] = index + 1 + quireAttributes[:certainty] = 1 + if group.parentID + quireAttributes[:parent] = idPrefix+"-q-"+parents.join("-") + end + xml.quire quireAttributes do + xml.text index + 1 + end + @groups[groupID]["xmlID"] = quireAttributes["xml:id"] + end + end + @leafIDs.each_with_index do |leafID, index| + leaf = project.leafs.find(leafID) + parents = parentsOrders(leafID, project) + leafemberOrder = parents.pop + idPostfix = parents.join("-")+"-"+leafemberOrder.to_s + leafAttributes = {} + leafAttributes["xml:id"] = idPrefix+"-"+idPostfix + leafAttributes["stub"] = "yes" if leaf.stubType != "None" + xml.leaf leafAttributes do + folioNumber = {} + folioNumber[:val] = @leafIDs.index(leafID)+1 + folioNumber[:certainty] = 1 + xml.folioNumber folioNumber do + xml.text folioNumber[:val].to_s + end + + mode = {} + if ['original', 'added', 'replaced', 'false', 'missing'].include? leaf.type.downcase + mode[:val] = leaf.type.downcase + mode[:certainty] = 1 + end + xml.mode mode + + qAttributes = {} + qAttributes[:position] = project.groups.find(leaf.parentID).memberIDs.index(leafID)+1 + qAttributes[:leafno] = leafemberOrder + qAttributes[:certainty] = 1 + qAttributes[:target] = "#"+idPrefix+"-q-"+parents.join("-") + qAttributes[:n] = parents[-1] + xml.q qAttributes do + if leaf.conjoined_to + idPostfix = parents.join("-")+"-"+@leafs[leaf.conjoined_to][:memberOrder].to_s + xml.conjoin :certainty => 1, :target => "#"+idPrefix+"-"+idPostfix + end + end + + if not leaf.conjoined_to + xml.single :val => "yes" + end + + rectoSide = project.sides.find(leaf.rectoID) + rectoAttributes = {} + rectoAttributes["xml:id"] = leafAttributes["xml:id"] + rectoAttributes[:type] = "Recto" + if rectoSide.folio_number + rectoAttributes[:folioNumber] = rectoSide.folio_number + else + rectoAttributes[:folioNumber] = folioNumber[:val].to_s+"R" + end + if rectoSide.page_number + rectoAttributes[:page_number] = rectoSide.page_number + else + rectoAttributes[:page_number] = "EMPTY" + end + rectoAttributes[:texture] = rectoSide.texture unless rectoSide.texture == "None" + rectoAttributes[:script_direction] = rectoSide.script_direction unless rectoSide.script_direction == "None" + rectoAttributes[:image] = rectoSide.image[:url] unless rectoSide.image.empty? + rectoAttributes[:target] = "#"+leafAttributes["xml:id"] + # xml.side rectoAttributes + @rectos[leaf.rectoID] = rectoAttributes + @rectos[leaf.rectoID]["recto"] = rectoSide + versoSide = project.sides.find(leaf.versoID) + versoAttributes = {} + versoAttributes["xml:id"] = leafAttributes["xml:id"] + versoAttributes[:type] = "Verso" + if versoSide.folio_number + versoAttributes[:folioNumber] = versoSide.folio_number + else + versoAttributes[:folioNumber] = folioNumber[:val].to_s+"R" + end + if versoSide.page_number + versoAttributes[:page_number] = versoSide.page_number + else + versoAttributes[:page_number] = "EMPTY" + end + versoAttributes[:texture] = versoSide.texture unless versoSide.texture == "None" + versoAttributes[:script_direction] = versoSide.script_direction unless versoSide.script_direction == "None" + versoAttributes[:image] = versoSide.image[:url] unless versoSide.image.empty? + versoAttributes[:target] = "#"+leafAttributes["xml:id"] + # xml.side versoAttributes + @versos[leaf.versoID] = versoAttributes + @versos[leaf.versoID]["verso"] = versoSide + end + @leafs[leafID]["xmlID"] = leafAttributes["xml:id"] + end + end + + # NOTES + if not project.notes.empty? + xml.notes do + project.notes.each_with_index do |note, index| + noteAttributes = {} + noteAttributes["xml:id"] = idPrefix+"-n-"+(index+1).to_s + noteAttributes[:type] = note.type + xml.note noteAttributes do + xml.text note.description + end + @notes[note.id.to_s] = {} + @notes[note.id.to_s]["xml:id"] = "#"+noteAttributes["xml:id"] + @notes[note.id.to_s][:note] = note + end + end + end + + # MAPPING + xml.mapping do + # Map quires to attributes and notes and memberIDs + @groupIDs.each do |groupID| + group = @groups[groupID] + parents = parentsOrders(groupID, project) + groupOrder = parents.pop + groupMemberOrder = group["memberOrder"] + idPrefix = project.shelfmark.parameterize.underscore + idPostfix = parents.empty? ? groupOrder.to_s : parents.join("-")+"-"+groupOrder.to_s + linkedNotes = (group.notes.map {|note| "#note_title"+"_"+note.title.parameterize.underscore}).join(" ") + linkedAttributes = [] + ['title', 'type', 'direction'].each do |attribute| + attributeValue = group[attribute] + if @allGroupAttributeValues.include? attributeValue + linkedAttributes.push("group_"+attribute+"_"+attributeValue.parameterize.underscore) + end + end + ['tacketed', 'sewing'].each do |attribute| + attributeValue = "" + group[attribute].each do |leafID| + parents = parentsOrders(leafID, project) + leafemberOrder = parents.pop + idPostfix = parents.join("-")+"-"+leafemberOrder.to_s + attributeValue = attributeValue + " #" + idPrefix+"-"+idPostfix + " " + attributeValue = attributeValue.strip + end + if @allGroupAttributeValues.include? attributeValue + parents = parentsOrders(groupID, project) + groupOrder = parents.pop + groupMemberOrder = group["memberOrder"] + idPostfix = parents.empty? ? groupOrder.to_s : parents.join("-")+"-"+groupOrder.to_s + linkedAttributes.push("group_"+attribute+"_"+idPrefix+"-q-"+idPostfix) + end + end + linkedAttributes = linkedAttributes.join(" #") + if linkedNotes+linkedAttributes != "" + xml.map :target => "#"+idPrefix+"-q-"+idPostfix do + if linkedAttributes != "" + xml.term :target => linkedNotes+" #"+linkedAttributes+" #group_members_"+idPrefix+"-q-"+idPostfix + else + xml.term :target => linkedNotes+" #group_members_"+idPrefix+"-q-"+idPostfix + end + end + end + end + # Map leaves to attributes and notes + @leafIDs.each do |leafID| + leaf = @leafs[leafID] + parents = parentsOrders(leafID, project) + leafemberOrder = parents.pop + idPostfix = parents.join("-")+"-"+leafemberOrder.to_s + linkedNotes = (leaf.notes.map {|note| "#note_title"+"_"+note.title.parameterize.underscore}).join(" ") + attachementMethods = [] + if leaf.attached_above != "None" + if leaf.attached_above == "Other" + attachementMethods.push("#Glued_Above_Other") + else + attachementMethods.push("#Glued_Above_"+leaf.attached_above.split(" ")[1][1..-2]) + end + end + if leaf.attached_below != "None" + if leaf.attached_below == "Other" + attachementMethods.push("#Glued_Below_Other") + else + attachementMethods.push("#Glued_Below_"+leaf.attached_below.split(" ")[1][1..-2]) + end + end + attachementMethods = attachementMethods.join(" ").strip + material = "" + if @allLeafAttributeValues.include? leaf.material and material != "" + material = "#leaf_material_"+leaf.material.parameterize.underscore.strip + end + if linkedNotes+attachementMethods+material != "" + xml.map :target => "#"+idPrefix+"-"+idPostfix do + xml.term :target => (linkedNotes+" "+material+attachementMethods).strip + end + end + end + # Map rectos to attributes and notes and sides + @rectos.each do |rectoID, attributes| + recto = attributes["recto"] + linkedNotes = (recto.notes.map {|note| "#note_title"+"_"+note.title.parameterize.underscore}).join(" ") + linkedImage = recto.image.empty? ? "" : recto.image[:url] + linkedAttributes = [] + ['texture', 'script_direction', 'page_number'].each do |attribute| + attributeValue = recto[attribute] + if @allSideAttributeValues.include? attributeValue and attributeValue + linkedAttributes.push("side_"+attribute+"_"+attributeValue.parameterize.underscore) + elsif attributeValue==nil and attribute=="page_number" and @allSideAttributeValues.include? "EMPTY" + linkedAttributes.push("side_"+attribute+"_EMPTY") + end + end + linkedAttributes = linkedAttributes.empty? ? "" : linkedAttributes.join(" #") + if linkedNotes+linkedImage+linkedAttributes.strip != "" + if linkedAttributes != "" + termText = linkedNotes.strip+" #"+linkedAttributes + if linkedImage != "" + termText = termText+" "+linkedImage+" #manifest_"+recto.image[:manifestID] + end + xml.map :side => 'recto', :target => "#"+attributes["xml:id"] do + xml.term :target => termText.strip + end + else + termText = linkedNotes.strip + if linkedImage != "" + termText = termText+" "+linkedImage+" #manifest_"+recto.image[:manifestID] + end + xml.map :side => 'recto', :target => "#"+attributes["xml:id"] do + xml.term :target => termText.strip + end + end + end + end + # Map versos to attributes and notes and sides + @versos.each do |versoID, attributes| + verso = attributes["verso"] + linkedNotes = (verso.notes.map {|note| "#note_title"+"_"+note.title.parameterize.underscore}).join(" ") + linkedImage = verso.image.empty? ? "" : verso.image[:url] + linkedAttributes = [] + ['texture', 'script_direction', 'page_number'].each do |attribute| + attributeValue = verso[attribute] + if @allSideAttributeValues.include? attributeValue and attributeValue + linkedAttributes.push("side_"+attribute+"_"+attributeValue.parameterize.underscore) + elsif attributeValue==nil and attribute=="page_number" and @allSideAttributeValues.include? "EMPTY" + linkedAttributes.push("side_"+attribute+"_EMPTY") + end + end + linkedAttributes = linkedAttributes.empty? ? "" : linkedAttributes.join(" #") + if linkedNotes+linkedImage+linkedAttributes.strip != "" + if linkedAttributes != "" + termText = linkedNotes.strip+" #"+linkedAttributes + if linkedImage != "" + termText = termText+" "+linkedImage+" #manifest_"+verso.image[:manifestID] + end + xml.map :side => 'verso', :target => "#"+attributes["xml:id"] do + xml.term :target => termText.strip + end + else + termText = linkedNotes.strip + if linkedImage != "" + termText = termText+" "+linkedImage+" #manifest_"+verso.image[:manifestID] + end + xml.map :side => 'verso', :target => "#"+attributes["xml:id"] do + xml.term :target => termText.strip + end + end + end + end + # Map notes to noteTitles + @notes.each do |noteID, attributes| + note = attributes[:note] + xml.map :target => attributes["xml:id"] do + termText = [] + if @noteTitles.include? note.title + termText.push("note_title"+"_"+note.title.parameterize.underscore) + end + termText = termText.empty? ? "" : termText.join(" #") + note.show ? termText=termText+" #note_show" : nil + xml.term :target => "#"+termText.strip + end + end + end + + end + }.to_xml + end + + + # Populate leaf and side objects in ascending order + def populateLeafSideObjects(memberIDs, project, leafMember=1) + groupMember = 1 + memberIDs.each_with_index do | memberID, index | + if memberID[0] == "G" + @groups[memberID] = {"memberOrder": groupMember} + populateLeafSideObjects(project.groups.find(memberID).memberIDs, project, leafMember) + groupMember += 1 + elsif memberID[0] == "L" + if not @leafIDs.include? memberID + leaf = project.leafs.find(memberID) + @leafIDs.push(memberID) + @leafs[memberID] = leaf + @leafs[memberID]["memberOrder"] = leafMember + @rectos[leaf.rectoID] = project.sides.find(leaf.rectoID) + @versos[leaf.versoID] = project.sides.find(leaf.versoID) + leafMember += 1 + end + end + end + end + + + # Get all parent orders upto root + def parentsOrders(memberID, project) + result = [] + if memberID + if memberID[0] == "G" + result = parentsOrders(project.groups.find(memberID).parentID, project) + [(@groupIDs.index(memberID)+1).to_s] + else + result = parentsOrders(project.leafs.find(memberID).parentID, project) + [@leafs[memberID][:memberOrder].to_s] + end + end + return result + end + + + + end +end diff --git a/viscoll-api/app/helpers/controller_helper/filter_helper.rb b/viscoll-api/app/helpers/controller_helper/filter_helper.rb new file mode 100644 index 00000000..18b0b3dd --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/filter_helper.rb @@ -0,0 +1,76 @@ +module ControllerHelper + module FilterHelper + + def runValidations(queries) + errors = [] + haveErrors = false + if queries == [] + return ["should contain at least 1 query"] + end + queries.each_with_index do |query, index| + error = {type: "", attribute: "", condition: "", values: "", conjunction: ""} + if (qc = query_types['type'][query['type']]).nil? + error['type'] = "type should be one of: [#{query_types['type'].keys.join(', ')}]" + haveErrors = true + elsif (qc = qc[query['attribute']]).nil? + error['attribute'] = "valid attributes for #{query['type']}: [#{query_types['type'][query['type']].keys.join(', ')}]" + haveErrors = true + elsif not qc.include?(query['condition']) + error['condition'] = "valid conditions for #{query['type']} attribute #{query['attribute']} : [#{qc.join(', ')}]" + haveErrors = true + end + + if queries.length > 1 && index { + 'group' => { + 'type' => ['equals', 'not equals'], + 'title' => ['equals', 'not equals', 'contains', 'not contains'] + }, + 'leaf' => { + 'type' => ['equals', 'not equals'], + 'material' => ['equals', 'not equals'], + 'conjoined_to' => ['equals', 'not equals'], + 'conjoined_leaf_order' => ['equals', 'not equals'], # Legacy attribute + 'attached_above' => ['equals', 'not equals'], + 'attached_below' => ['equals', 'not equals'], + 'stub' => ['equals', 'not equals'] + }, + 'side' => { + 'folio_number' => ['equals', 'not equals', 'contains', 'not contains'], + 'page_number' => ['equals', 'not equals', 'contains', 'not contains'], + 'texture' => ['equals', 'not equals'], + 'script_direction' => ['equals', 'not equals'], + 'uri' => ['equals', 'not equals', 'contains', 'not contains'], + }, + 'note' => { + 'title' => ['equals', 'not equals', 'contains', 'not contains'], + 'type' => ['equals', 'not equals'], + 'description' => ['equals', 'not equals', 'contains', 'not contains'] + } + }, + 'conjunction' => ['AND', 'OR'] + } + end + + end +end diff --git a/viscoll-api/app/helpers/controller_helper/groups_helper.rb b/viscoll-api/app/helpers/controller_helper/groups_helper.rb new file mode 100644 index 00000000..57c02e36 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/groups_helper.rb @@ -0,0 +1,49 @@ +module ControllerHelper + module GroupsHelper + include ControllerHelper::LeafsHelper + + def addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut, leafIDs=false, sideIDs=false) + begin + if (leafIDs and sideIDs) + Leaf.skip_callback(:create, :before, :create_sides) + end + newlyAddedLeafs = [] + newlyAddedLeafIDs = [] + sideIDIndex = 0 + noOfLeafs.times do |leafIDIndex| + leaf = Leaf.new({project_id: project_id, parentID:group.id.to_s, nestLevel: group.nestLevel}) + if (leafIDs and sideIDs) + leaf.id = leafIDs[leafIDIndex] + end + leaf.save() + newlyAddedLeafs.push(leaf) + newlyAddedLeafIDs.push(leaf.id.to_s) + # Create new sides for this leaf with given SideIDs + if (leafIDs and sideIDs) + recto = Side.new({parentID: leaf.id.to_s, project: leaf.project, texture: "Hair", id: sideIDs[sideIDIndex]}) + verso = Side.new({parentID: leaf.id.to_s, project: leaf.project, texture: "Flesh", id: sideIDs[sideIDIndex+1] }) + recto.id = "Recto_"+recto.id.to_s + verso.id = "Verso_"+verso.id.to_s + recto.save + verso.save + leaf.rectoID = recto.id + leaf.versoID = verso.id + leaf.save + end + sideIDIndex += 2 + end + # Add newly created leaves to this group + group.add_members(newlyAddedLeafIDs, 1) + # Auto-Conjoin newly added leaves in this group + if conjoin + autoConjoinLeaves(newlyAddedLeafs, oddMemberLeftOut) + end + rescue + ensure + if (leafIDs and sideIDs) + Leaf.set_callback(:create, :before, :create_sides) + end + end + end + end +end diff --git a/viscoll-api/app/helpers/controller_helper/import_helper.rb b/viscoll-api/app/helpers/controller_helper/import_helper.rb new file mode 100644 index 00000000..3381d65a --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/import_helper.rb @@ -0,0 +1,219 @@ +module ControllerHelper + module ImportHelper + + # JSON IMPORT + def handleJSONImport(data) + # reference variables + allGroupsIDsInOrder = [] + allLeafsIDsInOrder = [] + allRectosIDsInOrder = [] + allVersosIDsInOrder = [] + + # Create the Project + begin + Project.find_by(title: data["project"]["title"]) + data["project"]["title"] = "Copy of " + data["project"]["title"]+" @ " + Time.now.to_s + rescue Exception => e + end + data["project"]["user_id"] = current_user.id + project = Project.create(data["project"]) + + # Create all Leafs + data["Leafs"].each do |leafOrder, data| + data["params"]["project_id"] = project.id + leaf = Leaf.create(data["params"]) + allLeafsIDsInOrder.push(leaf.id.to_s) + allRectosIDsInOrder.push(leaf.rectoID) + allVersosIDsInOrder.push(leaf.versoID) + end + + # Create all Groups + data["Groups"].each do |groupOrder, data| + tacketed, sewing = [], [] + data["tacketed"].each do |leafOrder| + tacketed.push(allLeafsIDsInOrder[leafOrder-1]) + end + data["sewing"].each do |leafOrder| + sewing.push(allLeafsIDsInOrder[leafOrder-1]) + end + data["params"]["tacketed"] = tacketed + data["params"]["sewing"] = sewing + data["params"]["project_id"] = project.id + group = Group.create(data["params"]) + allGroupsIDsInOrder.push(group.id.to_s) + end + + project.reload + # Update all Group membersIDs and parentID + data["Groups"].each do |groupOrder, data| + group = project.groups.find(allGroupsIDsInOrder[groupOrder.to_i-1]) + parentID = data["parentOrder"] ? allGroupsIDsInOrder[data["parentOrder"]-1] : nil + memberIDs = [] + data["memberOrders"].each do |memberOrder| + memberType, memberOrder = memberOrder.split("_") + if memberType=="Group" + memberIDs.push(allGroupsIDsInOrder[memberOrder.to_i-1]) + else + memberIDs.push(allLeafsIDsInOrder[memberOrder.to_i-1]) + leaf = project.leafs.find(allLeafsIDsInOrder[memberOrder.to_i-1]) + leaf.update(parentID: group.id.to_s) + end + end + group.update(parentID: parentID, memberIDs: memberIDs) + end + + # Update all leafs with correct conjoinedTo leafID + data["Leafs"].each do |leafOrder, data| + if data["conjoined_leaf_order"] + leafIDConjoinedTo = allLeafsIDsInOrder[data["conjoined_leaf_order"]-1] + leaf = project.leafs.find(allLeafsIDsInOrder[leafOrder.to_i-1]) + leaf.update(conjoined_to: leafIDConjoinedTo) + end + end + + # Update all Rectos + allRectosIDsInOrder.each_with_index do |rectoID, order| + recto = project.sides.find(rectoID) + rectoParams = data["Rectos"][(order+1).to_s]["params"] + recto.update(rectoParams) + end + + # Update all Verso + allVersosIDsInOrder.each_with_index do |versoID, order| + verso = project.sides.find(versoID) + versoParams = data["Versos"][(order+1).to_s]["params"] + verso.update(versoParams) + end + + project.reload + # Create all Notes + data["Notes"].each do |noteOrder, data| + data["params"]["project_id"] = project.id + note = Note.new(data["params"]) + # Generate objectIDs of Groups, Leafs, Rectos, Versos with this note + groupIDs = [] + data["objects"]["Group"].each do |groupOrder| + groupID = allGroupsIDsInOrder[groupOrder-1] + group = project.groups.find(groupID) + group.notes.push(note) + group.save + groupIDs.push(groupID) + end + leafIDs = [] + data["objects"]["Leaf"].each do |leafOrder| + leafID = allLeafsIDsInOrder[leafOrder-1] + leaf = project.leafs.find(leafID) + leaf.notes.push(note) + leaf.save + leafIDs.push(leafID) + end + rectoIDs = [] + data["objects"]["Recto"].each do |rectoOrder| + rectoID = allRectosIDsInOrder[rectoOrder-1] + recto = project.sides.find(rectoID) + recto.notes.push(note) + recto.save + rectoIDs.push(rectoID) + end + versoIDs = [] + data["objects"]["Verso"].each do |versoOrder| + versoID = allVersosIDsInOrder[versoOrder-1] + verso = project.sides.find(versoID) + verso.notes.push(note) + verso.save + versoIDs.push(versoID) + end + note.objects[:Group] = groupIDs + note.objects[:Leaf] = leafIDs + note.objects[:Recto] = rectoIDs + note.objects[:Verso] = versoIDs + note.save + end + + # Update project groupIDs + project.groupIDs = allGroupsIDsInOrder + project.save + end + + + + # XML IMPORT + def handleXMLImport(data, xml) + # reference variables + allGroupsIDsInOrder = [] + allLeafsIDsInOrder = [] + allRectosIDsInOrder = [] + allVersosIDsInOrder = [] + @groups = {} + @leafs = {} + @rectos = {} + @versos = {} + + allGroups = xml.xpath('//x:quire', "x" => "http://schoenberginstitute.org/schema/collation") + allLeaves = xml.xpath('//x:leaf', "x" => "http://schoenberginstitute.org/schema/collation") + allNotes = xml.xpath('//x:note', "x" => "http://schoenberginstitute.org/schema/collation") + + # Create the Project + projectInformation = {} + projectInformation[:title] = data["title"] + if not projectInformation[:title] + projectInformation[:title] = "XML_Import_@_" + Time.now.to_s + end + begin + Project.find_by(title: projectInformation[:title]) + projectInformation[:title] = "Copy of " + projectInformation[:title] + " @ " + Time.now.to_s + rescue Exception => e + end + projectInformation[:shelfmark] = data["shelfmark"] + projectInformation[:metadata] = {date: data["date"]} + + # p projectInformation + # @project = Project.create(projectInformation) + allLeaves.each do |leaf| + leafID = nil + leafAttributes = {} + leaf.attributes.each do |attr| + if attr[1].name == "id" + leafID = attr[1].value + end + leafAttributes[attr[1].name] = attr[1].value + end + leafChildren = {} + leaf.getChildren.each do |child| + childAttributes = {} + child.attributes.each do |attr| + if attr[1].name == "id" + leafID = attr[1].value + end + childAttributes[attr[1].name] = attr[1].value + end + end + @leafs[leafID] = leafAttributes + end + p @leafs + + end + + def getAttributes(node) + attributes = node.attributes.dup + attributes.keys.each do |key| + attributes[key.to_sym] = attributes.delete(key).to_s + end + return attributes + end + + def getChildren(node) + return node.children.filter { |child| child.next_element.class != 'Nokogiri::XML::Text' } + end + + def getNodeID(node) + node.attributes.each do |attr| + if attr[1].name == "id" + return attr[1].value + end + end + end + + + end +end \ No newline at end of file diff --git a/viscoll-api/app/helpers/controller_helper/import_json_helper.rb b/viscoll-api/app/helpers/controller_helper/import_json_helper.rb new file mode 100644 index 00000000..bd73c42a --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/import_json_helper.rb @@ -0,0 +1,138 @@ +module ControllerHelper + module ImportJsonHelper + + # JSON IMPORT + def handleJSONImport(data) + # reference variables + allGroupsIDsInOrder = [] + allLeafsIDsInOrder = [] + allRectosIDsInOrder = [] + allVersosIDsInOrder = [] + + # Create the Project + begin + Project.find_by(title: data["project"]["title"]) + data["project"]["title"] = "Copy of " + data["project"]["title"]+" @ " + Time.now.to_s + rescue Exception => e + end + data["project"]["user_id"] = current_user.id + project = Project.create(data["project"]) + + # Create all Leafs + data["Leafs"].each do |leafOrder, data| + data["params"]["project_id"] = project.id + leaf = Leaf.create(data["params"]) + allLeafsIDsInOrder.push(leaf.id.to_s) + allRectosIDsInOrder.push(leaf.rectoID) + allVersosIDsInOrder.push(leaf.versoID) + end + + # Create all Groups + data["Groups"].each do |groupOrder, data| + tacketed, sewing = [], [] + data["tacketed"].each do |leafOrder| + tacketed.push(allLeafsIDsInOrder[leafOrder-1]) + end + data["sewing"].each do |leafOrder| + sewing.push(allLeafsIDsInOrder[leafOrder-1]) + end + data["params"]["tacketed"] = tacketed + data["params"]["sewing"] = sewing + data["params"]["project_id"] = project.id + group = Group.create(data["params"]) + allGroupsIDsInOrder.push(group.id.to_s) + end + + project.reload + # Update all Group membersIDs and parentID + data["Groups"].each do |groupOrder, data| + group = project.groups.find(allGroupsIDsInOrder[groupOrder.to_i-1]) + parentID = data["parentOrder"] ? allGroupsIDsInOrder[data["parentOrder"]-1] : nil + memberIDs = [] + data["memberOrders"].each do |memberOrder| + memberType, memberOrder = memberOrder.split("_") + if memberType=="Group" + memberIDs.push(allGroupsIDsInOrder[memberOrder.to_i-1]) + else + memberIDs.push(allLeafsIDsInOrder[memberOrder.to_i-1]) + leaf = project.leafs.find(allLeafsIDsInOrder[memberOrder.to_i-1]) + leaf.update(parentID: group.id.to_s) + end + end + group.update(parentID: parentID, memberIDs: memberIDs) + end + + # Update all leafs with correct conjoinedTo leafID + data["Leafs"].each do |leafOrder, data| + if data["conjoined_leaf_order"] + leafIDConjoinedTo = allLeafsIDsInOrder[data["conjoined_leaf_order"]-1] + leaf = project.leafs.find(allLeafsIDsInOrder[leafOrder.to_i-1]) + leaf.update(conjoined_to: leafIDConjoinedTo) + end + end + + # Update all Rectos + allRectosIDsInOrder.each_with_index do |rectoID, order| + recto = project.sides.find(rectoID) + rectoParams = data["Rectos"][(order+1).to_s]["params"] + recto.update(rectoParams) + end + + # Update all Verso + allVersosIDsInOrder.each_with_index do |versoID, order| + verso = project.sides.find(versoID) + versoParams = data["Versos"][(order+1).to_s]["params"] + verso.update(versoParams) + end + + project.reload + # Create all Notes + data["Notes"].each do |noteOrder, data| + data["params"]["project_id"] = project.id + note = Note.new(data["params"]) + # Generate objectIDs of Groups, Leafs, Rectos, Versos with this note + groupIDs = [] + data["objects"]["Group"].each do |groupOrder| + groupID = allGroupsIDsInOrder[groupOrder-1] + group = project.groups.find(groupID) + group.notes.push(note) + group.save + groupIDs.push(groupID) + end + leafIDs = [] + data["objects"]["Leaf"].each do |leafOrder| + leafID = allLeafsIDsInOrder[leafOrder-1] + leaf = project.leafs.find(leafID) + leaf.notes.push(note) + leaf.save + leafIDs.push(leafID) + end + rectoIDs = [] + data["objects"]["Recto"].each do |rectoOrder| + rectoID = allRectosIDsInOrder[rectoOrder-1] + recto = project.sides.find(rectoID) + recto.notes.push(note) + recto.save + rectoIDs.push(rectoID) + end + versoIDs = [] + data["objects"]["Verso"].each do |versoOrder| + versoID = allVersosIDsInOrder[versoOrder-1] + verso = project.sides.find(versoID) + verso.notes.push(note) + verso.save + versoIDs.push(versoID) + end + note.objects[:Group] = groupIDs + note.objects[:Leaf] = leafIDs + note.objects[:Recto] = rectoIDs + note.objects[:Verso] = versoIDs + note.save + end + + # Update project groupIDs + project.groupIDs = allGroupsIDsInOrder + project.save + end + end +end \ No newline at end of file diff --git a/viscoll-api/app/helpers/controller_helper/import_mapping_helper.rb b/viscoll-api/app/helpers/controller_helper/import_mapping_helper.rb new file mode 100644 index 00000000..e8efad2a --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/import_mapping_helper.rb @@ -0,0 +1,112 @@ +require 'zip' + +module ControllerHelper + module ImportMappingHelper + def decodeZip(imageData) + # Get the zip + tempzip = Tempfile.new('images.zip') + tempzip.binmode + regexp = /\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m + parts = imageData.match(regexp) + data = StringIO.new(Base64.decode64(parts[-1] || "")) + while part = data.read(16*1024) + tempzip.write(part) + end + tempzip.rewind + return tempzip + end + + def handleMappingImport(newProject, imageData, current_user) + begin + uploadedImages = {} + if imageData.present? + tempzip = decodeZip(imageData) + Zip::File.open(tempzip.path) do |zip_file| + zip_file.each do |file| + # Go through each file and collect its info + # The exported filename structure is: userGivenFilename_fileID.fileExtension + filename = file.name.rpartition('_')[0] + fileID = file.name.rpartition('_')[2].split('.')[0] + extension = file.name.split('.')[1] + tempfile = Tempfile.new("#{filename}") + tempfile.binmode + tempfile.write file.get_input_stream.read + tempfile.rewind + uploadedImages["#{filename}.#{extension}"] = {:fileID => fileID, :file => tempfile, :extension => extension} + end + end + end + # Go though all the sides in the newProject that are mapped to DIYImages. + # If it is not linked to Image that belongs to the current_user, unlink; otherwise update the link. + newProject.sides.each do |side| + if !side.image.empty? and side.image["manifestID"]=="DIYImages" + imageID = side.image["url"].split("/")[-1].split("_", 2)[0] + filename = side.image["url"].split("/")[-1].split("_", 2)[1] + image = current_user.images.where(:id => imageID).first + if not image + # Image object doesn't exist for current_user + # Check if any Image with 'filename' was uploaded during import. + if uploadedImages.key?(filename) + # Check if filename already exists for current_user + existingImage = current_user.images.where(:filename => filename).first + if existingImage + # Check if this new Image is different from the existing Image + if uploadedImages[filename][:fileID] == existingImage.fileID + # Same Image, so link this Image to the Side + side.image["url"]=@base_api_url+"/images/"+existingImage.id.to_s+"_"+existingImage.filename + side.save + !(existingImage.sideIDs.include?(side.id.to_s)) ? existingImage.sideIDs.push(side.id.to_s) : nil + !(existingImage.projectIDs.include?(newProject.id.to_s)) ? existingImage.projectIDs.push(newProject.id.to_s) : nil + existingImage.save + else + # Different Image, but with already existing filename. Rename the newImage and link to this Side. + filenameOnly = filename.rpartition(".")[0] + newFilename = "#{filenameOnly}(copy).#{uploadedImages[filename][:extension]}" + # check if filename already exists, if it does, add another "(copy)" + imageWithFilename = current_user.images.where(:filename => newFilename).first + while imageWithFilename + newFilename = "#{newFilename.rpartition(".")[0]}(copy).#{uploadedImages[filename][:extension]}" + imageWithFilename = current_user.images.where(:filename => newFilename).first + end + uploader = Shrine.new(:store) + uploaded_file = uploader.upload(uploadedImages[filename][:file], metadata: {"filename"=>newFilename, "mime_type": "image/#{uploadedImages[filename][:extension]}"}) + newImage = Image.new(user: current_user, filename: newFilename, fileID: uploaded_file.id, metadata: uploaded_file.metadata, projectIDs: [newProject.id.to_s]) + side.image["url"]=@base_api_url+"/images/"+newImage.id.to_s+"_"+newFilename + side.save + !(newImage.sideIDs.include?(side.id.to_s)) ? newImage.sideIDs.push(side.id.to_s) : nil + !(newImage.projectIDs.include?(newProject.id.to_s)) ? newImage.projectIDs.push(newProject.id.to_s) : nil + newImage.save + end + else + # Image object doesn't exist with filename + # Create Image + uploader = Shrine.new(:store) + uploaded_file = uploader.upload(uploadedImages[filename][:file], metadata: {"filename"=>"#{filename}", "mime_type": "image/#{uploadedImages[filename][:extension]}"}) + newImage = Image.new(user: current_user, filename: filename, fileID: uploaded_file.id, metadata: uploaded_file.metadata, projectIDs: [newProject.id.to_s]) + side.image["url"]=@base_api_url+"/images/"+newImage.id.to_s+"_"+newImage.filename + side.save + !(newImage.sideIDs.include?(side.id.to_s)) ? newImage.sideIDs.push(side.id.to_s) : nil + !(newImage.projectIDs.include?(newProject.id.to_s)) ? newImage.projectIDs.push(newProject.id.to_s) : nil + newImage.save + end + else + # No Image with with 'filename' was uploaded. So unlink this Side from existing mapping. + side.image = {} + side.save + end + else + # Image already exists with the curent_user. Link that Image to this Side. + side.image["url"]=@base_api_url+"/images/"+image.id.to_s+"_"+image.filename + side.save + !(image.sideIDs.include?(side.id.to_s)) ? image.sideIDs.push(side.id.to_s) : nil + !(image.projectIDs.include?(newProject.id.to_s)) ? image.projectIDs.push(newProject.id.to_s) : nil + image.save + end + end + end + rescue Exception => e + p e.message + end + end + end +end diff --git a/viscoll-api/app/helpers/controller_helper/import_xml_helper.rb b/viscoll-api/app/helpers/controller_helper/import_xml_helper.rb new file mode 100644 index 00000000..30d1e90e --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/import_xml_helper.rb @@ -0,0 +1,387 @@ +require 'uri' + +module ControllerHelper + module ImportXmlHelper + # XML IMPORT + def handleXMLImport(xml) + @allGroupNodeIDsInOrder = [] + @allLeafNodeIDsInOrder = [] + @groups = {} + @leafs = {} + @rectos = {} + @versos = {} + @notes = {} + + # Project Information + @projectInformation = { + title: "", + shelfmark: "", + metadata: {date: ""}, + preferences: {showTips: true}, + manifests: {}, + noteTypes: ["Unknown"] + } + # Grab project Title + projectTitleNode = xml.xpath("//x:title", "x" => "http://schoenberginstitute.org/schema/collation") + if projectTitleNode.text.empty? + @projectInformation[:title] = "No title" + else + @projectInformation[:title] = projectTitleNode.text + end + if not @projectInformation[:title] + @projectInformation[:title] = "XML_Import_@_" + Time.now.to_s + end + begin + Project.find_by(title: @projectInformation[:title]) + @projectInformation[:title] = "Copy of " + @projectInformation[:title] + " @ " + Time.now.to_s + rescue Exception => e + end + # grab project Shelfmark + projectShelfmarkNode = xml.xpath("//x:shelfmark", "x" => "http://schoenberginstitute.org/schema/collation") + @projectInformation[:shelfmark] = projectShelfmarkNode.text + # grap prohect Date + projectDateNode = xml.xpath("//x:date", "x" => "http://schoenberginstitute.org/schema/collation") + if not projectDateNode.empty? + @projectInformation[:metadata][:date] = projectDateNode.text + end + # Map manifests to Project + manifestTaxonomy = xml.xpath("//x:taxonomy[@xml:id='manifests']", "x" => "http://schoenberginstitute.org/schema/collation") + if not manifestTaxonomy.empty? + manifestTaxonomy.children.each do |child| + if child.name=="term" + id = child.attributes["id"].value.split("_")[-1] + url = child.text + @projectInformation[:manifests][id] = {:id => id, :url => url} + end + end + end + + # Groups Information + allGroupNodes = xml.xpath('//x:quire', "x" => "http://schoenberginstitute.org/schema/collation") + # Generate all attributes for Groups + allGroupNodes.each_with_index do |groupNode, index| + groupNodeID = groupNode.attributes["id"].value + parentNodeID = groupNode.attributes["parent"]? groupNode.attributes["parent"].value : nil + groupOrder = index+1 + @allGroupNodeIDsInOrder.push(groupNodeID) + nestLevel = 1 + while parentNodeID do + nodeSearchText = "//x:quire[@xml:id='"+parentNodeID+"']" + parentGroupNode = xml.xpath(nodeSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not parentGroupNode.empty? + parentNodeID = parentGroupNode[0].attributes["parent"]? parentGroupNode[0].attributes["parent"].value : nil + else + parentNodeID = nil + end + nestLevel += 1 + end + parentNodeID = groupNode.attributes["parent"]? groupNode.attributes["parent"].value : nil + parentOrder = parentNodeID ? @allGroupNodeIDsInOrder.index(parentNodeID)+1 : nil + @groups[groupOrder] = { + params: { + type: "Quire", + title: "", + direction: "", + nestLevel: nestLevel + }, + tacketed: [], + sewing: [], + parentOrder: parentOrder, + memberOrders: [], + noteTitles: [] + } + end + # MAP attributes for all groups + @groups.each do |groupOrder, attributes| + groupNodeID = @allGroupNodeIDsInOrder[groupOrder-1] + mapTargetSearchText = "//x:map[@target='#"+groupNodeID+"']" + groupMappingNodes = xml.xpath(mapTargetSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not groupMappingNodes.empty? + groupMappingNode = groupMappingNodes[0] # Only 1 mapping per group + groupTermTargets = groupMappingNode.children[1].attributes["target"].value.split(" ") + groupTermTargets.each do |target| + termSearchText = "//x:term[@xml:id='"+target[1..-1]+"']" + groupTerm = xml.xpath(termSearchText, "x" => "http://schoenberginstitute.org/schema/collation")[0] + groupTermTaxonomyID = groupTerm.parent.attributes["id"].value + groupTermTaxonomyID=="group_title" ? @groups[groupOrder][:params][:title]=groupTerm.text : nil + groupTermTaxonomyID=="group_type" ? @groups[groupOrder][:params][:type]=groupTerm.text : nil + groupTermTaxonomyID=="group_direction" ? @groups[groupOrder][:params][:direction]=groupTerm.text : nil + groupTermTaxonomyID=="group_sewing" ? @groups[groupOrder][:sewing]=groupTerm.text.split(" ") : nil + groupTermTaxonomyID=="group_tacketed" ? @groups[groupOrder][:tacketed]=groupTerm.text.split(" ") : nil + groupTermTaxonomyID=="group_members" ? @groups[groupOrder][:memberOrders]=groupTerm.text.split(" ") : nil + if groupTermTaxonomyID=="note_title" + @groups[groupOrder][:noteTitles].push(groupTerm.text) unless @groups[groupOrder][:noteTitles].include? groupTerm.text + end + end + end + end + + + # Generate all attributes for Leafs + allLeafNodes = xml.xpath('//x:leaf', "x" => "http://schoenberginstitute.org/schema/collation") + allLeafNodes.each_with_index do |leafNode, index| + leafNodeID = leafNode.attributes["id"].value + stub = leafNode.attributes["stub"] ? "Original" : "None" + type = "None" + conjoinedToNodeID = nil + leafOrder = index+1 + parentNodeID = nil + leafNode.children.each do |child| + if child.name == "mode" + type = child.attributes["val"] ? child.attributes["val"].value.capitalize : "None" + end + if child.name == "q" + parentNodeID = child.attributes["target"] ? child.attributes["target"].value : nil + child.children.each do |subChild| + if subChild.attributes["target"] + conjoinedToNodeID = subChild.attributes["target"].value[1..-1] + end + end + end + end + @allLeafNodeIDsInOrder.push(leafNodeID) + nestLevel = 1 + parentOrder = 1 + if parentNodeID + parentOrder = @allGroupNodeIDsInOrder.index(parentNodeID[1..-1])+1 + parentGroup = @groups[parentOrder] + nestLevel = parentGroup[:params][:nestLevel] + end + @leafs[leafOrder] = { + params: { + material: "None", + type: type, + attached_above: "None", + attached_below: "None", + stub: stub, + nestLevel: nestLevel + }, + conjoined_leaf_order: conjoinedToNodeID, + parentOrder: parentOrder, + rectoOrder: leafOrder, + versoOrder: leafOrder, + noteTitles: [] + } + @rectos[leafOrder] = { + params: { + folio_number: nil, + page_number: nil, + texture: "None", + image: {}, + script_direction: "None" + }, + parentOrder: leafOrder, + noteTitles: [] + } + @versos[leafOrder] = { + params: { + folio_number: nil, + page_number: nil, + texture: "None", + image: {}, + script_direction: "None" + }, + parentOrder: leafOrder, + noteTitles: [] + } + end + + # In @groups, Update sewing, tacketed and memberOrders from nodeIDs to globalOrders + @groups.each do |groupOrder, attributes| + sewing = attributes[:sewing].map {|leafNodeID| @allLeafNodeIDsInOrder.index(leafNodeID[1..-1])+1} + tacketed = attributes[:tacketed].map {|leafNodeID| @allLeafNodeIDsInOrder.index(leafNodeID[1..-1])+1} + memberOrders = [] + attributes[:memberOrders].each do |memberNodeID| + if memberNodeID.include? "q" + memberOrder = @allGroupNodeIDsInOrder.index(memberNodeID[1..-1])+1 + memberOrders.push("Group_"+memberOrder.to_s) + else + memberOrder = @allLeafNodeIDsInOrder.index(memberNodeID[1..-1])+1 + memberOrders.push("Leaf_"+memberOrder.to_s) + end + end + @groups[groupOrder][:sewing] = sewing + @groups[groupOrder][:tacketed] = tacketed + @groups[groupOrder][:memberOrders] = memberOrders + end + + # In @leafs, Update conjoined_to from nodeIDs to globalOrders. + # Also Map material, attachment_methods (for Leaves), texture, script_direction, page_number (for Sides) and noteTitles. + @leafs.each do |leafOrder, attributes| + if @leafs[leafOrder][:conjoined_leaf_order] + @leafs[leafOrder][:conjoined_leaf_order] = @allLeafNodeIDsInOrder.index(attributes[:conjoined_leaf_order])+1 + end + leafNodeID = @allLeafNodeIDsInOrder[leafOrder-1] + mapTargetSearchText = "//x:map[@target='#"+leafNodeID+"']" + leafMappingNodes = xml.xpath(mapTargetSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not leafMappingNodes.empty? + leafMappingNodes.each do |leafMappingNode| + if leafMappingNode.attributes["side"] + sideTermTargets = leafMappingNode.children[1].attributes["target"].value.split(" ") + sideTermTargets.each do |target| + if target =~ URI::regexp + # This is an Image URL Map + if leafMappingNode.attributes["side"].value=="recto" + @rectos[leafOrder][:params][:image][:url] = target + @rectos[leafOrder][:params][:image][:label] = target.split("/")[-1] + else + @versos[leafOrder][:params][:image][:url] = target + @versos[leafOrder][:params][:image][:label] = target.split("/")[-1] + end + elsif target[1..-1]=="manifest_DIYImages" + if leafMappingNode.attributes["side"].value=="recto" + @rectos[leafOrder][:params][:image][:manifestID]="DIYImages" + @rectos[leafOrder][:params][:image][:label] = @rectos[leafOrder][:params][:image][:label].split("_", 2)[1] + else + @versos[leafOrder][:params][:image][:manifestID]="DIYImages" + @versos[leafOrder][:params][:image][:label] = @versos[leafOrder][:params][:image][:label].split("_", 2)[1] + end + else + termSearchText = "//x:term[@xml:id='"+target[1..-1]+"']" + sideTerms = xml.xpath(termSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not sideTerms.empty? + sideTerm = sideTerms[0] + sideTermTaxonomyID = sideTerm.parent.attributes["id"].value + if leafMappingNode.attributes["side"].value=="recto" + sideTermTaxonomyID=="side_texture" ? @rectos[leafOrder][:params][:texture]=sideTerm.text : nil + sideTermTaxonomyID=="side_script_direction" ? @rectos[leafOrder][:params][:script_direction]=sideTerm.text : nil + sideTermTaxonomyID=="side_page_number" ? @rectos[leafOrder][:params][:page_number]=sideTerm.text : nil + sideTermTaxonomyID=="manifests" ? @rectos[leafOrder][:params][:image][:manifestID]=sideTerm.attributes["id"].value.split("_")[1] : nil + if sideTermTaxonomyID=="note_title" + @rectos[leafOrder][:noteTitles].push(sideTerm.text) unless @rectos[leafOrder][:noteTitles].include? sideTerm.text + end + else + sideTermTaxonomyID=="side_texture" ? @versos[leafOrder][:params][:texture]=sideTerm.text : nil + sideTermTaxonomyID=="side_script_direction" ? @versos[leafOrder][:params][:script_direction]=sideTerm.text : nil + sideTermTaxonomyID=="side_page_number" ? @versos[leafOrder][:params][:page_number]=sideTerm.text : nil + sideTermTaxonomyID=="manifests" ? @versos[leafOrder][:params][:image][:manifestID]=sideTerm.attributes["id"].value.split("_")[1] : nil + if sideTermTaxonomyID=="note_title" + @versos[leafOrder][:noteTitles].push(sideTerm.text) unless @versos[leafOrder][:noteTitles].include? sideTerm.text + end + end + end + end + end + else + leafTermTargets = leafMappingNode.children[1].attributes["target"].value.split(" ") + leafTermTargets.each do |target| + termSearchText = "//x:term[@xml:id='"+target[1..-1]+"']" + leafTerms = xml.xpath(termSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not leafTerms.empty? + leafTerm = leafTerms[0] + leafTermTaxonomyID = leafTerm.parent.attributes["id"].value + leafTermTaxonomyID=="leaf_material" ? @leafs[leafOrder][:params][:material]=leafTerm.text : nil + if leafTermTaxonomyID=="note_title" + @leafs[leafOrder][:noteTitles].push(leafTerm.text) unless @leafs[leafOrder][:noteTitles].include? leafTerm.text + end + if leafTermTaxonomyID=='leaf_attachment_method' + leafTerm.attributes["id"].value.include?("Above") ? @leafs[leafOrder][:params][:attached_above]=leafTerm.text : nil + leafTerm.attributes["id"].value.include?("Below") ? @leafs[leafOrder][:params][:attached_below]=leafTerm.text : nil + end + end + end + end + end + end + end + + # Generate all attributes for Notes + allNotes = xml.xpath('//x:note', "x" => "http://schoenberginstitute.org/schema/collation") + allNotes.each_with_index do |noteNode, noteOrder| + noteNodeID = noteNode.attributes["id"].value + type = noteNode.attributes["type"].value + title = "" + description = noteNode.text + show = false + @projectInformation[:noteTypes].push(type) + # MAP the noteTitle and show for all notes + mapTargetSearchText = "//x:map[@target='#"+noteNodeID+"']" + noteMappingNodes = xml.xpath(mapTargetSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not noteMappingNodes.empty? + noteMappingNode = noteMappingNodes[0] # Only 1 mapping per note + noteTermTargets = noteMappingNode.children[1].attributes["target"].value.split(" ") + noteTermTargets.each do |target| + termSearchText = "//x:term[@xml:id='"+target[1..-1]+"']" + noteTerms = xml.xpath(termSearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if not noteTerms.empty? + noteTerm = noteTerms[0] + noteTermTaxonomyID = noteTerm.parent.attributes["id"].value + noteTermTaxonomyID=="note_title" ? title=noteTerm.text : nil + noteTermTaxonomyID=="note_show" ? show=true : nil + end + end + end + # MAP Groups, Leafs, Rectos, Versos for this Note + groupOrders = [] + @groups.each do |groupOrder, attributes| + if attributes[:noteTitles].include? title + groupOrders.push(groupOrder) + end + end + leafOrders = [] + @leafs.each do |leafOrder, attributes| + if attributes[:noteTitles].include? title + leafOrders.push(leafOrder) + end + end + rectoOrders = [] + @rectos.each do |rectoOrder, attributes| + if attributes[:noteTitles].include? title + rectoOrders.push(rectoOrder) + end + end + versoOrders = [] + @versos.each do |versoOrder, attributes| + if attributes[:noteTitles].include? title + versoOrders.push(versoOrder) + end + end + @notes[noteOrder] = { + params: { + title: title, + type: type, + description: description, + show: show + }, + objects: { + Group: groupOrders, + Leaf: leafOrders, + Recto: rectoOrders, + Verso: versoOrders + } + } + end + + # Everything is fine upto this point unless the xml import is driectly from Dot's Model. + # In that case, we have to generate the memberOrders attribute for each Group manually. + # We will loose the actual memberOrders. Here we add the Group members first and then Leaf members. + taxonomySearchText = "//x:taxonomy[@xml:id='group_members']" + groupMembersTermNodes = xml.xpath(taxonomySearchText, "x" => "http://schoenberginstitute.org/schema/collation") + if groupMembersTermNodes.empty? + # Need to handle adding members to Groups + @groups.each do |groupOrder, attributes| + if attributes[:parentOrder] + @groups[attributes[:parentOrder]][:memberOrders].push("Group_"+groupOrder.to_s) + end + end + @leafs.each do |leafOrder, attributes| + if attributes[:parentOrder] + @groups[attributes[:parentOrder]][:memberOrders].push("Leaf_"+leafOrder.to_s) + end + end + end + + jsonImport = { + project: @projectInformation, + Groups: @groups, + Leafs: @leafs, + Rectos: @rectos, + Versos: @versos, + Notes: @notes + } + + handleJSONImport(JSON.parse(jsonImport.to_json)) + + end + end +end diff --git a/viscoll-api/app/helpers/controller_helper/leafs_helper.rb b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb new file mode 100644 index 00000000..a40ac773 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb @@ -0,0 +1,83 @@ +module ControllerHelper + module LeafsHelper + + # Auto-Conjoin the given leaves + def autoConjoinLeaves(leaves, oddLeafNumber) + targetLeaves = leaves.dup + leafIds = leaves.collect { |leaf| leaf.id.to_s } + if targetLeaves.size.odd? + oddLeaf = targetLeaves[oddLeafNumber-1] + unless oddLeaf.conjoined_to.blank? + @project.leafs.find(oddLeaf.conjoined_to).update(conjoined_to: nil) + oddLeaf.update(conjoined_to: nil) + end + targetLeaves.delete_at(oddLeafNumber-1) + leafIds.delete_at(oddLeafNumber-1) + end + targetLeaves.each do |leaf| + if leaf.conjoined_to && !leafIds.include?(leaf.conjoined_to) + old_conjoined_to_leaf = @project.leafs.find(leaf.conjoined_to) + if (old_conjoined_to_leaf.conjoined_to) + old_conjoined_to_leaf.update(conjoined_to: nil) + end + end + end + (targetLeaves.size/2).times do |i| + leafOne = targetLeaves[i] + leafTwo = targetLeaves[-i-1] + leafOne.update(conjoined_to: leafTwo.id.to_s) + leafTwo.update(conjoined_to: leafOne.id.to_s) + end + end + + def update_attached_to + parent = @project.groups.find(@leaf.parentID) + memberOrder = parent.memberIDs.index(@leaf.id.to_s) + if memberOrder > 0 + # This leaf is not the first leaf in the group + aboveLeaf = @project.leafs.find(parent.memberIDs[memberOrder-1]) + aboveLeaf.update(attached_below: @leaf.attached_above) + end + if memberOrder < parent.memberIDs.length - 1 + belowLeaf = @project.leafs.find(parent.memberIDs[memberOrder+1]) + belowLeaf.update(attached_above: @leaf.attached_below) + end + end + + def update_conjoined_partner(new_conjoined_to_leafID) + # VALIDATIONS + conjoinedToErrors = [] + begin + new_conjoined_to_leaf = @project.leafs.find(new_conjoined_to_leafID) + rescue Exception => e + if new_conjoined_to_leafID + conjoinedToErrors.push("leaf not found with id "+new_conjoined_to_leafID) + render json: {leaf: conjoinedToErrors}, status: :unprocessable_entity + return + end + end + if (@leaf.conjoined_to) + @old_conjoined_to_leaf = @project.leafs.find(@leaf.conjoined_to) + end + if (@old_conjoined_to_leaf) + @old_conjoined_to_leaf.update(conjoined_to: nil) + end + if new_conjoined_to_leaf + if (new_conjoined_to_leaf.conjoined_to) + new_conjoined_to_leaf_conjoined_to_leaf = @project.leafs.find(new_conjoined_to_leaf.conjoined_to) + if (new_conjoined_to_leaf_conjoined_to_leaf) + new_conjoined_to_leaf_conjoined_to_leaf.update(conjoined_to: nil) + end + end + new_conjoined_to_leaf.update(conjoined_to: @leaf.id.to_s) + end + end + + def handle_paper_update(leaf) + recto = @project.sides.find(leaf.rectoID) + verso = @project.sides.find(leaf.versoID) + recto.update(:texture => "None") + verso.update(:texture => "None") + end + end +end diff --git a/viscoll-api/app/helpers/controller_helper/projects_helper.rb b/viscoll-api/app/helpers/controller_helper/projects_helper.rb new file mode 100644 index 00000000..8f810e28 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/projects_helper.rb @@ -0,0 +1,261 @@ +require 'net/http' +module ControllerHelper + module ProjectsHelper + include ControllerHelper::LeafsHelper + def addGroupsLeafsConjoin(project, allGroups, folioNumber, pageNumber, startingTexture) + groupIDs = [] + allGroups.each do |groupInfo| + direction = groupInfo["direction"] + group = Group.new({project_id: project, title:"Default", type:"Quire", direction: direction}) + + # Create leaves + newlyAddedLeafs = [] + newlyAddedLeafIDs = [] + groupInfo["leaves"].times do |i| + leaf = Leaf.new({project_id: project, parentID: "Group_" + group.id.to_s}) + leaf.save() + newlyAddedLeafs.push(leaf) + newlyAddedLeafIDs.push(leaf.id.to_s) + end + # Add newly created leaves to this group + group = group.add_members(newlyAddedLeafIDs, 1, false) + # Auto-Conjoin newly added leaves in this group + if groupInfo["conjoin"] + autoConjoinLeaves(newlyAddedLeafs, groupInfo["oddLeaf"]) + end + group.save + groupIDs.push(group.id.to_s) + # Add folio numbers + if folioNumber + newlyAddedLeafs.each do |leaf| + recto = Side.find(leaf.rectoID) + verso = Side.find(leaf.versoID) + recto.update_attribute(:folio_number, folioNumber.to_s+"R") + verso.update_attribute(:folio_number, folioNumber.to_s+"V") + folioNumber += 1 + end + elsif pageNumber + newlyAddedLeafs.each do |leaf| + recto = Side.find(leaf.rectoID) + verso = Side.find(leaf.versoID) + recto.update_attribute(:page_number, pageNumber.to_s) + pageNumber += 1 + verso.update_attribute(:page_number, pageNumber.to_s) + pageNumber += 1 + end + end + # Assign side texture + assignTexture(newlyAddedLeafs, startingTexture) + end + # Add groups to project + project.add_groupIDs(groupIDs, 0) + end + + def getManifestInformation(url) + images = [] + begin + response = JSON.parse(Net::HTTP.get(URI(url))) + response["sequences"][0]["canvases"].each do |canvas| + images.push({label: canvas["label"], url: canvas["images"][0]["resource"]["service"]["@id"]}) + end + rescue + return {name: "Unparseable manifest URL", images: images} + end + return {name: response["label"][0..150], images: images} + end + + def assignTexture(leaves, startingTexture) + # Create pattern of hair and flesh depending on starting texture value + textures = [startingTexture] + textureOptions = [] + if startingTexture == "Hair" + textureOptions += ["Flesh", "Hair"] + else + textureOptions += ["Hair", "Flesh"] + end + leaves.count.times do |i| + textures += [textureOptions[i%2], textureOptions[i%2]] + end + # Update sides to have hair/flesh + i = 0 + leaves.each do | leaf| + recto = Side.find(leaf.rectoID) + verso = Side.find(leaf.versoID) + if leaf.conjoined_to != nil + recto.update_attribute(:texture, textures[i]) + i += 1 + verso.update_attribute(:texture, textures[i]) + i += 1 + else + recto.update_attribute(:texture, "Hair") + verso.update_attribute(:texture, "Flesh") + end + end + end + + def generateResponse() + @project.reload + @projectInformation = {} + @groupIDs = @project.groupIDs + @leafIDs = [] + @rectoIDs = [] + @versoIDs = [] + @groups = {} + @leafs = {} + @rectos = {} + @versos = {} + @notes = {} + + @projectInformation = { + "id": @project.id.to_s, + "title": @project.title, + "shelfmark": @project.shelfmark, + "metadata": @project.metadata, + "preferences": @project.preferences, + "manifests": @project.manifests, + "noteTypes": @project.noteTypes + } + @project.manifests.each do |manifestID, manifest| + manifestInformation = getManifestInformation(manifest[:url]) + manifestName = manifest[:name] ? manifest[:name] : manifestInformation[:name] + if manifestName.length>50 + manifestName = manifestName[0,47] + "..." + end + @projectInformation[:manifests][manifestID][:images] = manifestInformation[:images].map { |image| image.merge({manifestID: manifestID})} + @projectInformation[:manifests][manifestID][:name] = manifestName + end + # Generate all DIY images for this Project + @diyImages = [] + User.find(@project.user_id).images.all.each do |image| + if image.projectIDs.include? @project.id.to_s + @diyImages.push({ + label: image.filename, + url: @base_api_url+"/images/"+image.id.to_s+"_"+image.filename, + manifestID: "DIYImages" + }) + end + end + # @projectInformation[:manifests][:DIYImages] = { + # id: "DIYImages", + # images: @diyImages, + # name: "Uploaded Images" + # } + + @groupIDs.each_with_index do | groupID, index| + group = @project.groups.find(groupID) + # group = Group.find(groupID) + @groups[group.id.to_s] = { + "id": group.id.to_s, + "type": group.type, + "direction": group.direction, + "title": group.title, + "tacketed": group.tacketed, + "sewing": group.sewing, + "nestLevel": group.nestLevel, + "parentID": group.parentID, + "notes": [], + "memberIDs": group.memberIDs, + "memberType": "Group", + } + end + @groups.each do | groupID, group | + if group[:nestLevel] == 1 + getLeafMembers(group[:memberIDs]) + end + end + @project.leafs.each do | leaf | + @leafs[leaf.id.to_s] = { + "id": leaf.id.to_s, + "material": leaf.material, + "type": leaf.type, + "conjoined_to": leaf.conjoined_to, + "attached_above": leaf.attached_above, + "attached_below": leaf.attached_below, + "stub": leaf.stub, + "nestLevel": leaf.nestLevel, + "parentID": leaf.parentID, + "rectoID": leaf.rectoID, + "versoID": leaf.versoID, + "notes": [], + "memberType": "Leaf", + } + end + + + + @project.sides.each do | side | + parentOrder = @leafIDs.index(side.parentID) + 1 + obj = { + "id": side.id.to_s, + "parentID": side.parentID, + "folio_number": side.folio_number, + "page_number": side.page_number, + "texture": side.texture, + "image": side.image, + "script_direction": side.script_direction, + "notes": [], + "memberType": side.id[0] == "R" ? "Recto" : "Verso" + } + if side.id[0] == "R" + @rectos[side.id.to_s] = obj + elsif side.id[0] == "V" + @versos[side.id.to_s] = obj + end + end + + # Generate list of recto and verso ID's + @leafIDs.each do | leafID | + leaf = @leafs[leafID] + @rectoIDs.push(leaf[:rectoID]) + @versoIDs.push(leaf[:versoID]) + end + + @project.notes.each do | note | + @notes[note.id.to_s] = { + "id": note.id.to_s, + "title": note.title, + "type": note.type, + "description": note.description, + "show": note.show, + "objects": note.objects, + } + note.objects["Group"].each do | id | + @groups[id][:notes].append(note.id.to_s) + end + note.objects["Leaf"].each do | id | + @leafs[id][:notes].append(note.id.to_s) + end + note.objects["Recto"].each do | id | + @rectos[id][:notes].append(note.id.to_s) + end + note.objects["Verso"].each do | id | + @versos[id][:notes].append(note.id.to_s) + end + end + + return { + "project": @projectInformation, + "groupIDs": @groupIDs, + "leafIDs": @leafIDs, + "rectoIDs": @rectoIDs, + "versoIDs": @versoIDs, + "groups": @groups, + "leafs": @leafs, + "rectos": @rectos, + "versos": @versos, + "notes": @notes, + } + end + + # Populate @leafIDs recursively + def getLeafMembers(memberIDs) + memberIDs.each_with_index do | memberID, index | + if memberID[0] == "G" + getLeafMembers(@groups[memberID][:memberIDs]) + elsif memberID[0] == "L" + @leafIDs.push(memberID) + end + end + end + end +end diff --git a/viscoll-api/app/helpers/validation_helper/group_validation_helper.rb b/viscoll-api/app/helpers/validation_helper/group_validation_helper.rb new file mode 100644 index 00000000..cbecf78d --- /dev/null +++ b/viscoll-api/app/helpers/validation_helper/group_validation_helper.rb @@ -0,0 +1,113 @@ +module ValidationHelper + module GroupValidationHelper + def validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLeafs, conjoin, oddMemberLeftOut) + additionalErrors = {noOfGroups:[], parentGroupID:[], memberOrder:[], noOfLeafs: [], conjoin: [], oddMemberLeftOut: []} + haveErrors = false + if (noOfGroups==nil) + additionalErrors[:noOfGroups].push("is required") + haveErrors = true + elsif (!noOfGroups.is_a?(Integer)) + additionalErrors[:noOfGroups].push("should be an Integer") + haveErrors = true + elsif (noOfGroups < 1 or noOfGroups > 999) + additionalErrors[:noOfGroups].push("should range from 1 to 999") + haveErrors = true + end + if parentGroupID != nil && !Group.where(id: parentGroupID).exists? + haveErrors = true + additionalErrors[:parentGroupID].push("group not found with id "+parentGroupID) + end + if (parentGroupID!=nil && memberOrder==nil) + additionalErrors[:memberOrder].push("is required") + haveErrors = true + elsif (parentGroupID!=nil && !memberOrder.is_a?(Integer)) + additionalErrors[:memberOrder].push("should be an Integer") + haveErrors = true + elsif (parentGroupID!=nil && memberOrder < 1) + additionalErrors[:memberOrder].push("should be greater than 0") + haveErrors = true + end + if (noOfLeafs != nil and !noOfLeafs.is_a?(Integer)) + additionalErrors[:noOfLeafs].push("should be an Integer") + haveErrors = true + elsif (noOfLeafs != nil and (noOfLeafs < 1 or noOfLeafs > 999)) + additionalErrors[:noOfLeafs].push("should range from 1 to 999") + haveErrors = true + end + if (conjoin != nil) + if (!conjoin.is_a?(Boolean)) + additionalErrors[:conjoin].push("should be a Boolean") + haveErrors = true + elsif (conjoin and (noOfLeafs != nil and noOfLeafs == 1)) + additionalErrors[:conjoin].push("should be false if the number of leaves is 1") + haveErrors = true + end + end + if (oddMemberLeftOut != nil) + if (!oddMemberLeftOut.is_a?(Integer)) + additionalErrors[:oddMemberLeftOut].push("should be an Integer") + haveErrors = true + elsif (oddMemberLeftOut < 1 or oddMemberLeftOut > noOfLeafs) + additionalErrors[:oddMemberLeftOut].push("should range from 1 to the number of leaves") + haveErrors = true + elsif (noOfLeafs.even?) + additionalErrors[:oddMemberLeftOut].push("should be empty if the number of leaves is even") + haveErrors = true + end + end + + if additionalErrors[:noOfGroups] == [] + additionalErrors = additionalErrors.without(:noOfGroups) + end + if additionalErrors[:parentGroupID] == [] + additionalErrors = additionalErrors.without(:parentGroupID) + end + if additionalErrors[:memberOrder] == [] + additionalErrors = additionalErrors.without(:memberOrder) + end + if additionalErrors[:noOfLeafs] == [] + additionalErrors = additionalErrors.without(:noOfLeafs) + end + if additionalErrors[:conjoin] == [] + additionalErrors = additionalErrors.without(:conjoin) + end + if additionalErrors[:oddMemberLeftOut] == [] + additionalErrors = additionalErrors.without(:oddMemberLeftOut) + end + return additionalErrors + end + + def validateGroupBatchDelete(allGroups) + errors = [] + allGroups.each do |groupID| + unless Group.where(id: groupID).exists? + errors.push("group not found with id "+groupID) + end + end + return errors + end + + def validateGroupBatchUpdate(allGroups) + errors = [] + allGroups.each do |group_params| + haveError = false + error = {id: [], attributes: {type: []}} + groupID = group_params[:id] + type = group_params[:attributes][:type] + unless Group.where(id: groupID).exists? + haveError = true + error[:id].push("group not found with id "+groupID) + end + if (type != nil and type!="Quire" and type!="Booklet") + error[:attributes][:type].push("should be either Quire or Booklet") + haveError = true + end + if haveError + errors.push(error) + end + end + return errors + end + + end +end diff --git a/viscoll-api/app/helpers/validation_helper/leaf_validation_helper.rb b/viscoll-api/app/helpers/validation_helper/leaf_validation_helper.rb new file mode 100644 index 00000000..00cc21b9 --- /dev/null +++ b/viscoll-api/app/helpers/validation_helper/leaf_validation_helper.rb @@ -0,0 +1,70 @@ +module ValidationHelper + module LeafValidationHelper + + def validateLeafParams(project_id, parentID) + leafErrors = {project_id: [], parentID: []} + if (project_id==nil) + leafErrors[:project_id].push("is required") + elsif (!project_id.is_a?(String)) + leafErrors[:project_id].push("should be a String") + else + begin + @project = Project.find(project_id) + rescue Exception => e + leafErrors[:project_id].push("project not found") + end + end + if (parentID==nil) + leafErrors[:parentID].push("is required") + elsif (!parentID.is_a?(String)) + leafErrors[:parentID].push("should be a String") + else + begin + @group = Group.find(parentID) + if (@group.project_id.to_s != project_id) + leafErrors[:parentID].push("Group with parentID does not have project_id as a member") + end + rescue Exception => e + leafErrors[:parentID].push("group not found") + end + end + return leafErrors + end + + def validateAdditionalLeafParams(project_id, parentGroupID, memberOrder, noOfLeafs, conjoin, oddMemberLeftOut) + additionalErrors = {memberOrder: [], noOfLeafs: [], conjoin: [], oddMemberLeftOut: []} + if (memberOrder==nil) + additionalErrors[:memberOrder].push("is required") + elsif (!memberOrder.is_a?(Integer)) + additionalErrors[:memberOrder].push("should be an Integer") + elsif (memberOrder < 1) + additionalErrors[:memberOrder].push("should be greater than 0") + end + if (noOfLeafs==nil) + additionalErrors[:noOfLeafs].push("is required") + elsif (!noOfLeafs.is_a?(Integer)) + additionalErrors[:noOfLeafs].push("should be an Integer") + elsif (noOfLeafs < 1 or noOfLeafs > 999) + additionalErrors[:noOfLeafs].push("should range from 1 to 999") + end + if (conjoin != nil) + if (!conjoin.is_a?(Boolean)) + additionalErrors[:conjoin].push("should be a Boolean") + elsif (conjoin and noOfLeafs == 1) + additionalErrors[:conjoin].push("should be false if the number of leaves is 1") + end + end + if (oddMemberLeftOut != nil) + if (!oddMemberLeftOut.is_a?(Integer)) + additionalErrors[:oddMemberLeftOut].push("should be an Integer") + elsif (oddMemberLeftOut < 1 or oddMemberLeftOut > noOfLeafs) + additionalErrors[:oddMemberLeftOut].push("should range from 1 to the number of leaves") + elsif (noOfLeafs.even?) + additionalErrors[:oddMemberLeftOut].push("should be present only if the number of leaves is odd") + end + end + return additionalErrors + end + + end +end diff --git a/viscoll-api/app/helpers/validation_helper/project_validation_helper.rb b/viscoll-api/app/helpers/validation_helper/project_validation_helper.rb new file mode 100644 index 00000000..1e6a7bcc --- /dev/null +++ b/viscoll-api/app/helpers/validation_helper/project_validation_helper.rb @@ -0,0 +1,50 @@ +module ValidationHelper + module ProjectValidationHelper + def validateProjectCreateGroupsParams(allGroups) + @group_errors = [] + if not allGroups + allGroups = [] + end + allGroups.each_with_index do |group, index| + haveGroupError = false + @group_error = {groupID: (index+1)} + @group_error[:leaves] = [] + @group_error[:oddLeaf] = [] + @group_error[:conjoin] = [] + leaves = group["leaves"] + oddLeaf = group["oddLeaf"] + conjoin = group["conjoin"] + if (!leaves.is_a?(Integer)) + @group_error[:leaves].push("should be an Integer") + haveGroupError = true + elsif (leaves < 1) + @group_error[:leaves].push("should be greater than 0") + haveGroupError = true + end + if (leaves.is_a?(Integer) and leaves.odd?) + if (!oddLeaf.is_a?(Integer)) + @group_error[:oddLeaf].push("should be an Integer") + haveGroupError = true + else + if (oddLeaf < 1) + @group_error[:oddLeaf].push("should be greater than 0") + haveGroupError = true + end + if (oddLeaf > leaves) + @group_error[:oddLeaf].push("cannot be greater than leaves") + haveGroupError = true + end + end + end + if (!conjoin.is_a?(Boolean)) + @group_error[:conjoin].push("should be a Boolean") + haveGroupError = true + end + if (haveGroupError) + @group_errors.push(@group_error) + end + end + return {status: @group_errors.empty?, errors: @group_errors} + end + end +end \ No newline at end of file diff --git a/app/jobs/application_job.rb b/viscoll-api/app/jobs/application_job.rb similarity index 100% rename from app/jobs/application_job.rb rename to viscoll-api/app/jobs/application_job.rb diff --git a/viscoll-api/app/mailers/account_approval_mailer.rb b/viscoll-api/app/mailers/account_approval_mailer.rb new file mode 100644 index 00000000..9b1e01a0 --- /dev/null +++ b/viscoll-api/app/mailers/account_approval_mailer.rb @@ -0,0 +1,11 @@ +class AccountApprovalMailer < ApplicationMailer + default from: RailsJwtAuth.mailer_sender + + def sendApprovalStatus(user) + @user = User.find(user) + mail( + subject: "VisColl Account Approval", + to: @user.email, + ) + end +end \ No newline at end of file diff --git a/app/mailers/application_mailer.rb b/viscoll-api/app/mailers/application_mailer.rb similarity index 57% rename from app/mailers/application_mailer.rb rename to viscoll-api/app/mailers/application_mailer.rb index 286b2239..0060a22f 100644 --- a/app/mailers/application_mailer.rb +++ b/viscoll-api/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: 'from@example.com' + default from: 'utlviscoll@library.utoronto.ca' layout 'mailer' end diff --git a/viscoll-api/app/mailers/feedback_mailer.rb b/viscoll-api/app/mailers/feedback_mailer.rb new file mode 100644 index 00000000..39b7d4b9 --- /dev/null +++ b/viscoll-api/app/mailers/feedback_mailer.rb @@ -0,0 +1,13 @@ +class FeedbackMailer < ApplicationMailer + def sendFeedback(title, message, browserInformation, projectJSONExport, current_user) + @title = title + @message = message + @browserInformation = browserInformation + @projectJSONExport = projectJSONExport + @user = User.find(current_user) + mail( + subject: title, + to:"utlviscoll@library.utoronto.ca", + ) + end +end diff --git a/viscoll-api/app/mailers/mailer.rb b/viscoll-api/app/mailers/mailer.rb new file mode 100644 index 00000000..37ae24af --- /dev/null +++ b/viscoll-api/app/mailers/mailer.rb @@ -0,0 +1,56 @@ +if defined?(ActionMailer) + class RailsJwtAuth::Mailer < ApplicationMailer + default from: RailsJwtAuth.mailer_sender + + def confirmation_instructions(user) + @user = user + if RailsJwtAuth.confirmation_url + url, params = RailsJwtAuth.confirmation_url.split('?') + params = params ? params.split('&') : [] + params.push("confirmation_token=#{@user.confirmation_token}") + + @confirmation_url = "#{url}?#{params.join('&')}" + else + @confirmation_url = confirmation_url(confirmation_token: @user.confirmation_token) + end + subject = I18n.t('rails_jwt_auth.mailer.confirmation_instructions.subject') + # mail(to: @user.unconfirmed_email || @user.email, subject: subject) + toEmail = Rails.application.secrets.admin_email || "dummy-admin@library.utoronto.ca" + mail(to: toEmail, subject: subject) + end + + def reset_password_instructions(user) + @user = user + + if RailsJwtAuth.reset_password_url + url, params = RailsJwtAuth.reset_password_url.split('?') + params = params ? params.split('&') : [] + params.push("reset_password_token=#{@user.reset_password_token}") + + @reset_password_url = "#{url}?#{params.join('&')}" + else + @reset_password_url = password_url(reset_password_token: @user.reset_password_token) + end + + subject = I18n.t('rails_jwt_auth.mailer.reset_password_instructions.subject') + mail(to: @user.email, subject: subject) + end + + def set_password_instructions(user) + @user = user + + if RailsJwtAuth.set_password_url + url, params = RailsJwtAuth.set_password_url.split('?') + params = params ? params.split('&') : [] + params.push("reset_password_token=#{@user.reset_password_token}") + + @reset_password_url = "#{url}?#{params.join('&')}" + else + @reset_password_url = password_url(reset_password_token: @user.reset_password_token) + end + + subject = I18n.t('rails_jwt_auth.mailer.set_password_instructions.subject') + mail(to: @user.email, subject: subject) + end + end +end \ No newline at end of file diff --git a/app/assets/javascripts/channels/.keep b/viscoll-api/app/models/concerns/.keep similarity index 100% rename from app/assets/javascripts/channels/.keep rename to viscoll-api/app/models/concerns/.keep diff --git a/viscoll-api/app/models/group.rb b/viscoll-api/app/models/group.rb new file mode 100644 index 00000000..994516ec --- /dev/null +++ b/viscoll-api/app/models/group.rb @@ -0,0 +1,79 @@ +class Group + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :title, type: String, default: "None" + field :type, type: String, default: "Quire" + field :direction, type: String + field :tacketed, type: Array, default: [] + field :sewing, type: Array, default: [] + field :nestLevel, type: Integer, default: 1 + field :parentID, type: String + field :memberIDs, type: Array, default: [] # eg [ id1, id2, ... ] + + # Relations + belongs_to :project + has_and_belongs_to_many :notes, inverse_of: nil + + # Callbacks + before_create :edit_ID + before_destroy :unlink_notes, :unlink_project, :unlink_group, :destroy_members + + + def edit_ID + self.id = "Group_"+self.id.to_s unless self.id.to_s[0] == "G" + end + + # Add new members to this group + def add_members(memberIDs, startOrder, save=true) + if self.memberIDs.length==0 + self.memberIDs = memberIDs + elsif + self.memberIDs.insert(startOrder-1, *memberIDs) + end + if save + self.save + end + return self + end + + def remove_members(ids) + newList = self.memberIDs.reject{|id| ids.include?(id)} + self.memberIDs = newList + self.save + end + + # If linked to note(s), remove link from the note(s)'s side + def unlink_notes + if self.notes + self.notes.each do | note | + note.objects[:Group].delete(self.id.to_s) + note.save + end + end + end + + # Remove itself from project + def unlink_project + self.project.remove_groupID(self.id.to_s) + end + + # Remove itself from parent group (if nested) + def unlink_group + if self.parentID != nil + Group.find(self.parentID).remove_members([self.id.to_s]) + end + end + + def destroy_members + self.memberIDs.each do | memberID | + if memberID[0] === "G" + Group.find(memberID).destroy + elsif memberID[0] === "L" + Leaf.find(memberID).destroy + end + end + end + +end diff --git a/viscoll-api/app/models/image.rb b/viscoll-api/app/models/image.rb new file mode 100644 index 00000000..7a17057f --- /dev/null +++ b/viscoll-api/app/models/image.rb @@ -0,0 +1,36 @@ +class Image + include Mongoid::Document + + # Fields + field :filename, type: String + field :fileID, type: String + field :metadata, type: Hash + field :projectIDs, type: Array, default: [] # List of projectIDs this image belongs to + field :sideIDs, type: Array, default: [] # List of sideIDs this image is mapped to + + # Relations + belongs_to :user, inverse_of: :images + + # Callbacks + before_destroy :unlink_sides_before_delete, :delete_file + validates_uniqueness_of :filename, :message => "Image with filename: '%{value}', already exists.", scope: :user + + protected + # If linked to side(s), remove link from the side(s) + def unlink_sides_before_delete + self.sideIDs.each do |sideID| + if side = Side.where(:id => sideID).first + side.image = {} + side.save + end + end + end + + def delete_file + path = "#{Rails.root}/public/uploads/#{self.fileID}" + if File.file?(path) + File.delete(path) + end + end + +end diff --git a/viscoll-api/app/models/leaf.rb b/viscoll-api/app/models/leaf.rb new file mode 100644 index 00000000..0d836263 --- /dev/null +++ b/viscoll-api/app/models/leaf.rb @@ -0,0 +1,85 @@ +class Leaf + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :material, type: String, default: "None" + field :type, type: String, default: "None" + field :conjoined_to, type: String + field :attached_above, type: String, default: "None" + field :attached_below, type: String, default: "None" + field :stubType, :as => :stub, type: String, default: "None" + field :parentID, type: String + field :nestLevel, type: Integer, default: 1 + field :rectoID, type: String + field :versoID, type: String + + # Relations + belongs_to :project + has_and_belongs_to_many :notes, inverse_of: nil + + # Callbacks + before_create :edit_ID, :create_sides + before_destroy :unlink_notes, :destroy_sides, :update_parent_group + + + # Remove itself from its parent group + def remove_from_group + Group.find(self.parentID).remove_members([self.id.to_s]) + end + + protected + def edit_ID + self.id = "Leaf_"+self.id.to_s unless self.id.to_s[0]=="L" + end + + # If linked to note(s), remove link from the note(s)'s side + def unlink_notes + self.notes.each do | note | + note.objects[:Leaf].delete(self.id.to_s) + note.save + end + end + + # Create 2 sides(Recto & Verso) for this new leaf. + def create_sides + recto = Side.new({parentID: self.id.to_s, project: self.project}) + verso = Side.new({parentID: self.id.to_s, project: self.project}) + recto.id = "Recto_"+recto.id.to_s + verso.id = "Verso_"+verso.id.to_s + recto.save + verso.save + self.rectoID = recto.id + self.versoID = verso.id + end + + # Destroy its two sides + def destroy_sides + Side.find(self.rectoID).destroy + Side.find(self.versoID).destroy + end + + def update_attached_to + project = Project.find(self.project_id) + parent = project.groups.find(self.parentID) + memberOrder = parent.memberIDs.index(self.id.to_s) + if memberOrder > 0 + # This leaf is not the first leaf in the group + aboveLeaf = project.leafs.find(parent.memberIDs[memberOrder-1]) + aboveLeaf.update(attached_below: self.attached_above) + end + if memberOrder < parent.memberIDs.length - 1 + belowLeaf = project.leafs.find(parent.memberIDs[memberOrder+1]) + belowLeaf.update(attached_above: self.attached_below) + end + end + + # Update leaf's parent Group's Tacketed & Sewing if it contains this leafID + def update_parent_group + group = Group.find(self.parentID) + group.tacketed.include?(self.id.to_s) ? group.tacketed.delete(self.id.to_s) : nil + group.sewing.include?(self.id.to_s) ? group.sewing.delete(self.id.to_s) : nil + group.save + end +end + diff --git a/viscoll-api/app/models/note.rb b/viscoll-api/app/models/note.rb new file mode 100644 index 00000000..84de2eb1 --- /dev/null +++ b/viscoll-api/app/models/note.rb @@ -0,0 +1,49 @@ +class Note + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :title, type: String, default: "None" + field :type, type: String, default: "" + field :description, type: String, default: "" + field :objects, type: Hash, default: {Group: [], Leaf: [], Recto: [], Verso: []} + field :show, type: Boolean, default: false + + # Relations + belongs_to :project, inverse_of: :notes + + # Validations + validates_presence_of :title, :message => "Note title is required." + validates_uniqueness_of :title, :message => "Note title should be unique.", scope: :project + validates_presence_of :type, :message => "Note type is required." + + # Callbacks + before_destroy :update_objects_before_delete + + def update_objects_before_delete + self.objects[:Group].each do |groupID| + if group = Group.where(:id => groupID).first + group.notes.delete(self) + group.save + end + end + self.objects[:Leaf].each do |leafID| + if leaf = Leaf.where(:id => leafID).first + leaf.notes.delete(self) + leaf.save + end + end + self.objects[:Recto].each do |sideID| + if side = Side.where(:id => sideID).first + side.notes.delete(self) + side.save + end + end + self.objects[:Verso].each do |sideID| + if side = Side.where(:id => sideID).first + side.notes.delete(self) + side.save + end + end + end +end diff --git a/viscoll-api/app/models/project.rb b/viscoll-api/app/models/project.rb new file mode 100644 index 00000000..54bcdc0f --- /dev/null +++ b/viscoll-api/app/models/project.rb @@ -0,0 +1,57 @@ +class Project + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :title, type: String + field :shelfmark, type: String # (eg) "MS 1754" + field :metadata, type: Hash, default: lambda { { } } # (eg) {date: "19th century"} + field :manifests, type: Hash, default: lambda { { } } # (eg) { "1234556": { id: "123456, url: ""} } + field :noteTypes, type: Array, default: ["Unknown"] # custom notetypes + field :preferences, type: Hash, default: lambda { { :showTips => true } } + field :groupIDs, type: Array, default: [] + + # Relations + belongs_to :user, inverse_of: :projects + has_many :groups, dependent: :delete + has_many :leafs, dependent: :delete + has_many :sides, dependent: :delete + has_many :notes, dependent: :delete + + # Callbacks + before_destroy :unlink_images_before_delete + + # Validations + validates_presence_of :title, :message => "Project title is required." + validates_uniqueness_of :title, :message => "Project title: '%{value}', must be unique.", scope: :user + + def add_groupIDs(groupIDs, index) + if self.groupIDs.length == 0 + self.groupIDs = groupIDs + else + self.groupIDs.insert(index, *groupIDs) + end + self.save() + end + + def remove_groupID(groupID) + self.groupIDs.delete(groupID) + self.save() + end + + def unlink_images_before_delete + Image.where(:user_id => self.user.id).each do |image| + # Unlink All Sides that belongs to this Project that has this Image mapped to it. + image.sideIDs.each do |sideID| + side = self.sides.where(:id => sideID).first + if side + side.image = {} + side.save + image.sideIDs.include?(sideID) ? image.sideIDs.delete(sideID) : nil + end + end + image.projectIDs.include?(self.id.to_s) ? image.projectIDs.delete(self.id.to_s) : nil + image.save + end + end +end diff --git a/viscoll-api/app/models/side.rb b/viscoll-api/app/models/side.rb new file mode 100644 index 00000000..727819dd --- /dev/null +++ b/viscoll-api/app/models/side.rb @@ -0,0 +1,39 @@ +class Side + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :folio_number, type: String, default: nil + field :page_number, type: String, default: nil + field :texture, type: String, default: "None" + field :script_direction, type: String, default: "None" + field :image, type: Hash, default: lambda { { } } # {manifestID: 123, label: "bla, " url: "https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3002_0001"} + field :parentID, type: String + + # Relations + belongs_to :project + has_and_belongs_to_many :notes, inverse_of: nil + + # Callbacks + before_destroy :unlink_notes, :unlink_image + + protected + # If linked to note(s), remove link from the note(s)'s side + def unlink_notes + self.notes.each do | note | + note.objects[:Recto].delete(self.id.to_s) + note.objects[:Verso].delete(self.id.to_s) + note.save + end + end + + # If linked to image, remove link from the image's sides list + def unlink_image + if not self.image.empty? + if (image = Image.where(:id => self.image[:url].split("/")[-1].split("_", 2)[0]).first) + image.sideIDs.delete(self.id.to_s) + image.save + end + end + end +end diff --git a/viscoll-api/app/models/user.rb b/viscoll-api/app/models/user.rb new file mode 100644 index 00000000..4b2df830 --- /dev/null +++ b/viscoll-api/app/models/user.rb @@ -0,0 +1,13 @@ +class User + include Mongoid::Document + include RailsJwtAuth::Authenticatable + include RailsJwtAuth::Confirmable + include RailsJwtAuth::Recoverable + include RailsJwtAuth::Trackable + + field :name, type: String, default: "" + + has_many :images, dependent: :destroy + has_many :projects, dependent: :destroy + +end diff --git a/viscoll-api/app/views/account_approval_mailer/sendApprovalStatus.html.erb b/viscoll-api/app/views/account_approval_mailer/sendApprovalStatus.html.erb new file mode 100644 index 00000000..44dac600 --- /dev/null +++ b/viscoll-api/app/views/account_approval_mailer/sendApprovalStatus.html.erb @@ -0,0 +1,12 @@ +
+
+ +
+
+ +
+

Hi <%= @user.name %>,

+

Congratulations! Your request to join VisColl has been successsfully approved.

+

You can now log in with the credentials that you used to register.

+
+
\ No newline at end of file diff --git a/viscoll-api/app/views/exports/show.json.jbuilder b/viscoll-api/app/views/exports/show.json.jbuilder new file mode 100644 index 00000000..ecaa5533 --- /dev/null +++ b/viscoll-api/app/views/exports/show.json.jbuilder @@ -0,0 +1,16 @@ +json.set! 'Export' do + json.project @data[:project] + json.Groups @data[:groups] + json.Leafs @data[:leafs] + json.Rectos @data[:rectos] + json.Versos @data[:versos] + json.Notes @data[:notes] +end + +json.set! 'Images' do + if @zipFilePath + json.exportedImages @zipFilePath + else + json.exportedImages "" + end +end \ No newline at end of file diff --git a/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb b/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb new file mode 100644 index 00000000..6cd9fc3e --- /dev/null +++ b/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb @@ -0,0 +1,21 @@ +
+
+ +
+
+ +
+

Feedback from: <%= @user.email %>

+

Message:
<%= @message %>

+
+

Browser Information:
+ <%= @browserInformation %> +

+
+ <% if @projectJSONExport!=nil %> +

Project JSON Export:
+ <%= @projectJSONExport %> +

+ <% end %> +
+
\ No newline at end of file diff --git a/viscoll-api/app/views/filter/show.json.jbuilder b/viscoll-api/app/views/filter/show.json.jbuilder new file mode 100644 index 00000000..fe28a93a --- /dev/null +++ b/viscoll-api/app/views/filter/show.json.jbuilder @@ -0,0 +1,11 @@ +json.Groups @groups +json.Leafs @leafs +json.Sides @sides +json.Notes @notes +json.GroupsOfMatchingLeafs @groupsOfMatchingLeafs +json.LeafsOfMatchingSides @leafsOfMatchingSides +json.GroupsOfMatchingSides @groupsOfMatchingSides +json.GroupsOfMatchingNotes @groupsOfMatchingNotes +json.LeafsOfMatchingNotes @leafsOfMatchingNotes +json.SidesOfMatchingNotes @sidesOfMatchingNotes +json.visibleAttributes @visibleAttributes diff --git a/app/views/layouts/mailer.html.erb b/viscoll-api/app/views/layouts/mailer.html.erb similarity index 100% rename from app/views/layouts/mailer.html.erb rename to viscoll-api/app/views/layouts/mailer.html.erb diff --git a/app/views/layouts/mailer.text.erb b/viscoll-api/app/views/layouts/mailer.text.erb similarity index 100% rename from app/views/layouts/mailer.text.erb rename to viscoll-api/app/views/layouts/mailer.text.erb diff --git a/viscoll-api/app/views/projects/index.json.jbuilder b/viscoll-api/app/views/projects/index.json.jbuilder new file mode 100644 index 00000000..0aebab9d --- /dev/null +++ b/viscoll-api/app/views/projects/index.json.jbuilder @@ -0,0 +1,13 @@ +json.set! "projects" do + json.array!(@projects.desc(:updated_at)) do | project | + json.extract! project, :id, :title, :shelfmark, :metadata, :created_at, :updated_at + end +end + +json.set! "images" do + json.array!(@images) do | image | + json.extract! image, :id, :projectIDs, :sideIDs + json.url @base_api_url+"/images/"+image.id.to_s+"_"+image.filename + json.label image.filename + end +end \ No newline at end of file diff --git a/viscoll-api/app/views/projects/show.json.jbuilder b/viscoll-api/app/views/projects/show.json.jbuilder new file mode 100644 index 00000000..2bdc2f6e --- /dev/null +++ b/viscoll-api/app/views/projects/show.json.jbuilder @@ -0,0 +1,44 @@ +json.set! "active" do + json.id @data[:project][:id] + json.title @data[:project][:title] + json.shelfmark @data[:project][:shelfmark] + json.metadata @data[:project][:metadata] + json.preferences @data[:project][:preferences] + json.noteTypes @data[:project][:noteTypes] + + json.set! "manifests" do + json.set! "DIYImages" do + json.id "DIYImages" + json.images @diyImages + json.name "Uploaded Images" + end + json.merge! @data[:project][:manifests] + end + + json.groupIDs @data[:groupIDs] + json.leafIDs @data[:leafIDs] + json.rectoIDs @data[:rectoIDs] + json.versoIDs @data[:versoIDs] + + json.Groups @data[:groups] + json.Leafs @data[:leafs] + json.Rectos @data[:rectos] + json.Versos @data[:versos] + json.Notes @data[:notes] +end + +json.set! "dashboard" do + json.set! "projects" do + json.array!(@projects.desc(:updated_at)) do | project | + json.extract! project, :id, :title, :shelfmark, :metadata, :created_at, :updated_at + end + end + + json.set! "images" do + json.array!(@images) do | image | + json.extract! image, :id, :projectIDs, :sideIDs + json.url @base_api_url+"/images/"+image.id.to_s+"_"+image.filename + json.label image.filename + end + end +end \ No newline at end of file diff --git a/viscoll-api/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb b/viscoll-api/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb new file mode 100644 index 00000000..468db6dc --- /dev/null +++ b/viscoll-api/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb @@ -0,0 +1,19 @@ +
+
+ +
+
+ +
+

Hello Admin,

+

The following user has requested to join VisColl. You can confirm the account through the link below.

+

Once successfully confirmed, the user will be notified by email.

+
+ +
+

Name: <%= @user.name %>

+

Email: <%= @user.email %>

+
+

<%= link_to 'Confirm Account', @confirmation_url.html_safe, {:style => 'color: #4ED6CB'} %>

+
+
\ No newline at end of file diff --git a/viscoll-api/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb b/viscoll-api/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb new file mode 100644 index 00000000..52ade26c --- /dev/null +++ b/viscoll-api/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb @@ -0,0 +1,16 @@ +
+
+ +
+
+
+

Hi <%= @user.name %>!

+

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', @reset_password_url.html_safe, {:style => 'color: #4ED6CB'} %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

+ +
+
\ No newline at end of file diff --git a/viscoll-api/app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb b/viscoll-api/app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb new file mode 100644 index 00000000..ab990d02 --- /dev/null +++ b/viscoll-api/app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb @@ -0,0 +1,13 @@ +
+
+ +
+
+
+

Hi <%= @user.name %>!

+ +

You need to define your password to complete registration. You can do this through the link below.

+ +

<%= link_to 'Set my password', @reset_password_url.html_safe, {:style => 'color: #4ED6CB'} %>

+
+
\ No newline at end of file diff --git a/viscoll-api/app/views/sessions/index.json.jbuilder b/viscoll-api/app/views/sessions/index.json.jbuilder new file mode 100644 index 00000000..ff05c143 --- /dev/null +++ b/viscoll-api/app/views/sessions/index.json.jbuilder @@ -0,0 +1,13 @@ +json.session do + json.jwt @userToken + json.id @user.id + json.email @user.email + json.name @user.name + json.lastLoggedIn @user.last_sign_in_at + + json.projects(@userProjects) do | project | + json.id project.id + json.merge! project.attributes.except("_id", "user_id") + end + +end diff --git a/viscoll-api/app/views/users/show.json.jbuilder b/viscoll-api/app/views/users/show.json.jbuilder new file mode 100644 index 00000000..05481d57 --- /dev/null +++ b/viscoll-api/app/views/users/show.json.jbuilder @@ -0,0 +1,5 @@ +json.extract! @user, :id, :name, :email +json.projects(@user.projects) do | project | + json.id project.id + json.merge! project.attributes.except("_id", "user_id") +end \ No newline at end of file diff --git a/bin/bundle b/viscoll-api/bin/bundle similarity index 100% rename from bin/bundle rename to viscoll-api/bin/bundle diff --git a/bin/rails b/viscoll-api/bin/rails similarity index 53% rename from bin/rails rename to viscoll-api/bin/rails index 07396602..5badb2fd 100755 --- a/bin/rails +++ b/viscoll-api/bin/rails @@ -1,4 +1,9 @@ #!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands' diff --git a/viscoll-api/bin/rake b/viscoll-api/bin/rake new file mode 100755 index 00000000..d87d5f57 --- /dev/null +++ b/viscoll-api/bin/rake @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/viscoll-api/bin/setup similarity index 100% rename from bin/setup rename to viscoll-api/bin/setup diff --git a/viscoll-api/bin/spring b/viscoll-api/bin/spring new file mode 100755 index 00000000..fb2ec2eb --- /dev/null +++ b/viscoll-api/bin/spring @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. + +unless defined?(Spring) + require 'rubygems' + require 'bundler' + + lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) + spring = lockfile.specs.detect { |spec| spec.name == "spring" } + if spring + Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path + gem 'spring', spring.version + require 'spring/binstub' + end +end diff --git a/bin/update b/viscoll-api/bin/update similarity index 100% rename from bin/update rename to viscoll-api/bin/update diff --git a/config.ru b/viscoll-api/config.ru similarity index 100% rename from config.ru rename to viscoll-api/config.ru diff --git a/config/application.rb b/viscoll-api/config/application.rb similarity index 50% rename from config/application.rb rename to viscoll-api/config/application.rb index 0b45f014..20dac125 100644 --- a/config/application.rb +++ b/viscoll-api/config/application.rb @@ -1,4 +1,5 @@ require_relative 'boot' +require_relative 'shrine' require "rails" # Pick the frameworks you want: @@ -9,17 +10,36 @@ require "action_mailer/railtie" require "action_view/railtie" require "action_cable/engine" -require "sprockets/railtie" +# require "sprockets/railtie" # require "rails/test_unit/railtie" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) -module ViscollObns +module ViscollApi class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + + # Only loads a smaller set of middleware suitable for API only apps. + # Middleware like session, flash, cookies can be added back manually. + # Skip views, helpers and assets when generating a new resource. + config.api_only = true + + Mongo::Logger.logger.level = Logger::FATAL + config.log_level = :warn + + # Rack CORS for handling Cross-Origin Resource Sharing (CORS) + config.middleware.use Rack::Cors do + allow do + origins '*' + resource '*', + :headers => :any, + :expose => ['access-token', 'expiry', 'token-type', 'uid', 'client'], + :methods => [:get, :patch, :put, :delete, :post, :options] + end + end end end diff --git a/config/boot.rb b/viscoll-api/config/boot.rb similarity index 100% rename from config/boot.rb rename to viscoll-api/config/boot.rb diff --git a/config/cable.yml b/viscoll-api/config/cable.yml similarity index 100% rename from config/cable.yml rename to viscoll-api/config/cable.yml diff --git a/config/environment.rb b/viscoll-api/config/environment.rb similarity index 100% rename from config/environment.rb rename to viscoll-api/config/environment.rb diff --git a/config/environments/development.rb b/viscoll-api/config/environments/development.rb similarity index 74% rename from config/environments/development.rb rename to viscoll-api/config/environments/development.rb index 58d20de6..397a2e9b 100644 --- a/config/environments/development.rb +++ b/viscoll-api/config/environments/development.rb @@ -30,17 +30,18 @@ config.action_mailer.raise_delivery_errors = false config.action_mailer.perform_caching = false + config.action_mailer.delivery_method = :smtp + # config.action_mailer.default_url_options = { :host => "localhost", :port => 3000 } + config.action_mailer.smtp_settings = { + :address => 'smtp.ethereal.email', + :port => 587, + :user_name => 'libby.corkery17@ethereal.email', + :password => 'RP4P6zMm3rVW9adMZF' + } # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true - - # Suppress logger output for asset requests. - config.assets.quiet = true # Raises error for missing translations # config.action_view.raise_on_missing_translations = true @@ -48,4 +49,12 @@ # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker + + config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + resource '*', :headers => :any, :methods => [:get, :post, :put, :patch, :options, :delete] + end + end + end diff --git a/config/environments/production.rb b/viscoll-api/config/environments/production.rb similarity index 88% rename from config/environments/production.rb rename to viscoll-api/config/environments/production.rb index 5db900a8..4355c7d5 100644 --- a/config/environments/production.rb +++ b/viscoll-api/config/environments/production.rb @@ -18,14 +18,6 @@ # Apache or NGINX already handles this. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier - # config.assets.css_compressor = :sass - - # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false - - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.action_controller.asset_host = 'http://assets.example.com' @@ -54,8 +46,14 @@ # Use a real queuing backend for Active Job (and separate queues per environment) # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "ViscollObns_#{Rails.env}" + # config.active_job.queue_name_prefix = "viscoll-api_#{Rails.env}" config.action_mailer.perform_caching = false + config.action_mailer.default_url_options = { :host => "utlviscoll.library.utoronto.ca" } + config.action_mailer.smtp_settings = { + address: 'mailer.library.utoronto.ca', + port: 25, + enable_starttls_auto: false + } # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. diff --git a/config/environments/test.rb b/viscoll-api/config/environments/test.rb similarity index 95% rename from config/environments/test.rb rename to viscoll-api/config/environments/test.rb index 30587ef6..7c76952d 100644 --- a/config/environments/test.rb +++ b/viscoll-api/config/environments/test.rb @@ -33,6 +33,7 @@ # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.action_mailer.default_url_options = { :host => "localhost", :port => 3000 } # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr diff --git a/config/initializers/application_controller_renderer.rb b/viscoll-api/config/initializers/application_controller_renderer.rb similarity index 100% rename from config/initializers/application_controller_renderer.rb rename to viscoll-api/config/initializers/application_controller_renderer.rb diff --git a/config/initializers/backtrace_silencers.rb b/viscoll-api/config/initializers/backtrace_silencers.rb similarity index 100% rename from config/initializers/backtrace_silencers.rb rename to viscoll-api/config/initializers/backtrace_silencers.rb diff --git a/viscoll-api/config/initializers/cors.rb b/viscoll-api/config/initializers/cors.rb new file mode 100644 index 00000000..3b1c1b5e --- /dev/null +++ b/viscoll-api/config/initializers/cors.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +# Rails.application.config.middleware.insert_before 0, Rack::Cors do +# allow do +# origins 'example.com' +# +# resource '*', +# headers: :any, +# methods: [:get, :post, :put, :patch, :delete, :options, :head] +# end +# end diff --git a/config/initializers/filter_parameter_logging.rb b/viscoll-api/config/initializers/filter_parameter_logging.rb similarity index 100% rename from config/initializers/filter_parameter_logging.rb rename to viscoll-api/config/initializers/filter_parameter_logging.rb diff --git a/config/initializers/inflections.rb b/viscoll-api/config/initializers/inflections.rb similarity index 100% rename from config/initializers/inflections.rb rename to viscoll-api/config/initializers/inflections.rb diff --git a/config/initializers/mime_types.rb b/viscoll-api/config/initializers/mime_types.rb similarity index 100% rename from config/initializers/mime_types.rb rename to viscoll-api/config/initializers/mime_types.rb diff --git a/viscoll-api/config/initializers/mongoid.rb b/viscoll-api/config/initializers/mongoid.rb new file mode 100644 index 00000000..9172e037 --- /dev/null +++ b/viscoll-api/config/initializers/mongoid.rb @@ -0,0 +1,11 @@ +module BSON + class ObjectId + def to_json(*args) + to_s.to_json + end + + def as_json(*args) + to_s.as_json + end + end +end diff --git a/config/initializers/new_framework_defaults.rb b/viscoll-api/config/initializers/new_framework_defaults.rb similarity index 64% rename from config/initializers/new_framework_defaults.rb rename to viscoll-api/config/initializers/new_framework_defaults.rb index e34c66ce..351d7371 100644 --- a/config/initializers/new_framework_defaults.rb +++ b/viscoll-api/config/initializers/new_framework_defaults.rb @@ -4,18 +4,16 @@ # # Read the Guide for Upgrading Ruby on Rails for more info on each option. -# Enable per-form CSRF tokens. Previous versions had false. -Rails.application.config.action_controller.per_form_csrf_tokens = true - -# Enable origin-checking CSRF mitigation. Previous versions had false. -Rails.application.config.action_controller.forgery_protection_origin_check = true +Rails.application.config.raise_on_unfiltered_parameters = true # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. # Previous versions had false. ActiveSupport.to_time_preserves_timezone = true # Do not halt callback chains when a callback returns false. Previous versions had true. -ActiveSupport.halt_callback_chains_on_return_false = false +# DEPRECATION WARNING: ActiveSupport.halt_callback_chains_on_return_false= is deprecated +# and will be removed in Rails 5.2. +# ActiveSupport.halt_callback_chains_on_return_false = false # Configure SSL options to enable HSTS with subdomains. Previous versions had false. Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/viscoll-api/config/initializers/rails_jwt_auth.rb b/viscoll-api/config/initializers/rails_jwt_auth.rb new file mode 100644 index 00000000..15b7f935 --- /dev/null +++ b/viscoll-api/config/initializers/rails_jwt_auth.rb @@ -0,0 +1,38 @@ +RailsJwtAuth.setup do |config| + # authentication model class name + #config.model_name = 'User' + + # field name used to authentication with password + #config.auth_field_name = 'email' + + # set to true to validate auth_field email format + #config.auth_field_email = true + + # expiration time for generated tokens + #config.jwt_expiration_time = 7.days + + # the "iss" (issuer) claim identifies the principal that issued the JWT + #config.jwt_issuer = 'RailsJwtAuth' + + # number of simultaneously sessions for an user + #config.simultaneously_sessions = 3 + + # mailer sender + config.mailer_sender = 'noreply-dummy@library.utoronto.ca' + + # url used to create email link with confirmation token + config.confirmation_url = if Rails.env.production? then 'https://dummy.library.utoronto.ca/confirmation' else 'http://127.0.0.1:3000/confirmation' end + + # expiration time for confirmation tokens + #config.confirmation_expiration_time = 1.day + + # url used to create email link with reset password token + config.reset_password_url = if Rails.env.production? then 'https://dummy.library.utoronto.ca/password' else 'http://127.0.0.1:3000/password' end + + + # expiration time for reset password tokens + #config.reset_password_expiration_time = 1.day + + # uses deliver_later to send emails instead of deliver method + #config.deliver_later = false +end diff --git a/config/initializers/wrap_parameters.rb b/viscoll-api/config/initializers/wrap_parameters.rb similarity index 100% rename from config/initializers/wrap_parameters.rb rename to viscoll-api/config/initializers/wrap_parameters.rb diff --git a/config/locales/en.yml b/viscoll-api/config/locales/en.yml similarity index 100% rename from config/locales/en.yml rename to viscoll-api/config/locales/en.yml diff --git a/config/mongoid.yml b/viscoll-api/config/mongoid.yml similarity index 87% rename from config/mongoid.yml rename to viscoll-api/config/mongoid.yml index b4f74dd1..b4db75e2 100644 --- a/config/mongoid.yml +++ b/viscoll-api/config/mongoid.yml @@ -5,11 +5,11 @@ development: default: # Defines the name of the default database that Mongoid can connect to. # (required). - database: viscoll_obns_development + database: viscoll_api_development # Provides the hosts the default client can connect to. Must be an array # of host:port pairs. (required) hosts: - - localhost:27017 + - mongo:27017 options: # Change the default write concern. (default = { w: 1 }) # write: @@ -34,8 +34,9 @@ development: # - 'dbOwner' # Change the default authentication mechanism. Valid options are: :scram, - # :mongodb_cr, :mongodb_x509, and :plain. (default on 3.0 is :scram, default - # on 2.4 and 2.6 is :plain) + # :mongodb_cr, :mongodb_x509, and :plain. Note that all authentication + # mechanisms require username and password, with the exception of :mongodb_x509. + # Default on mongoDB 3.0 is :scram, default on 2.4 and 2.6 is :plain. # auth_mech: :scram # The database or source to authenticate the user against. @@ -121,6 +122,10 @@ development: # existing method. (default: false) # scope_overwrite_exception: false + # Raise an error when defining a field with the same name as an + # existing method. (default: false) + # duplicate_fields_exception: false + # Use Active Support's time zone in conversions. (default: true) # use_activesupport_time_zone: true @@ -132,15 +137,18 @@ development: # otherwise.(default: :info) # log_level: :info + # Control whether `belongs_to` association is required. By default + # `belongs_to` will trigger a validation error if the association + # is not present. (default: true) + # belongs_to_required_by_default: true + # Application name that is printed to the mongodb logs upon establishing a # connection in server versions >= 3.4. Note that the name cannot exceed 128 bytes. # app_name: MyApplicationName test: clients: default: - database: viscoll_obns_test - hosts: - - localhost:27017 + uri: mongodb://localhost:27017/viscoll_test options: read: mode: :primary diff --git a/config/puma.rb b/viscoll-api/config/puma.rb similarity index 100% rename from config/puma.rb rename to viscoll-api/config/puma.rb diff --git a/viscoll-api/config/routes.rb b/viscoll-api/config/routes.rb new file mode 100644 index 00000000..789aa716 --- /dev/null +++ b/viscoll-api/config/routes.rb @@ -0,0 +1,66 @@ +Rails.application.routes.draw do + + # AUTHENTICATION ENDPOINTS + resource :session, controller: 'sessions', only: [:create, :destroy], defaults: {format: :json} + resource :registration, controller: 'registrations', only: [:create], defaults: {format: :json} + resource :registration, controller: 'rails_jwt_auth/registrations', only: [:create, :update, :destroy] + resource :password, controller: 'rails_jwt_auth/passwords', only: [:create, :update] + resource :confirmation, controller: 'rails_jwt_auth/confirmations', only: [:create] + resource :confirmation, controller: 'confirmations', only: [:update] + + # USER ENDPOINTS + resources :users, defaults: {format: :json}, only: [:show, :update, :destroy] + post '/feedback', to: 'feedback#create', defaults: {format: :json} + + # PROJECT ENDPOINTS + put '/projects/:id/filter', to: 'filter#show', defaults: {format: :json} + get '/projects/:id/export/:format', to: 'export#show', defaults: {format: :json} + get '/projects/:id/clone', to: 'projects#clone', defaults: {format: :json} + put '/projects/import', to: 'import#index', defaults: {format: :json} + post '/projects/:id/manifests', to: 'projects#createManifest', defaults: {format: :json} + put '/projects/:id/manifests', to: 'projects#updateManifest', defaults: {format: :json} + delete '/projects/:id/manifests', to: 'projects#deleteManifest', defaults: {format: :json} + get '/projects/:id/viewOnly', to: 'projects#viewOnly', defaults: {format: :json} + resources :projects, defaults: {format: :json}, only: [:index, :show, :update, :destroy, :create] + + # DIY IMAGE ENDPOINTS + post '/images', to: 'images#uploadImages', defaults: {format: :json} + put '/images/link', to: 'images#link', defaults: {format: :json} + put '/images/unlink', to: 'images#unlink', defaults: {format: :json} + get '/images/:imageID_filename', to: 'images#show', defaults: {format: :json} + get '/images/zip/:id', to: 'images#getZipImages', defaults: {format: :json} + delete '/images', to: 'images#destroy', defaults: {format: :json} + + # GROUP ENDPOINTS + resources :groups, defaults: {format: :json}, only: [:update, :destroy, :create] + put '/groups', to: 'groups#updateMultiple', defaults: {format: :json}, only: [:update] + delete '/groups', to: 'groups#destroyMultiple', defaults: {format: :json}, only: [:destroy] + + # LEAF ENDPOINTS + put '/leafs/conjoin', to: 'leafs#conjoinLeafs', defaults: {format: :json}, only: [:update] + put '/leafs', to: 'leafs#updateMultiple', defaults: {format: :json}, only: [:update] + delete '/leafs', to: 'leafs#destroyMultiple', defaults: {format: :json}, only: [:destroy] + resources :leafs, defaults: {format: :json}, only: [:update, :destroy, :create] + + # SIDE ENDPOINTS + put '/sides/generateFolio', to: 'sides#generateFolio', defaults: {format: :json}, only: [:update] + put '/sides/generatePageNumber', to: 'sides#generatePageNumber', defaults: {format: :json}, only: [:update] + put '/sides/:id', to: 'sides#update', defaults: {format: :json}, only: [:update] + put '/sides', to: 'sides#updateMultiple', defaults: {format: :json}, only: [:update] + + # NOTE ENDPOINTS + put '/notes/:id/link', to: 'notes#link', defaults: {format: :json}, only: [:update] + put '/notes/:id/unlink', to: 'notes#unlink', defaults: {format: :json}, only: [:update] + post '/notes/type', to: 'notes#createType', defaults: {format: :json}, only: [:create] + put '/notes/type', to: 'notes#updateType', defaults: {format: :json}, only: [:update] + delete '/notes/type', to: 'notes#deleteType', defaults: {format: :json}, only: [:destroy] + resources :notes, defaults: {format: :json}, only: [:show, :update, :destroy, :create] + + # DOCUMENTATION + get '/docs' => redirect('/docs/index.html') + + # ROOT ENPOINT + get '/', to: proc { [200, {}, ['']] } + + +end diff --git a/config/secrets.yml b/viscoll-api/config/secrets.yml similarity index 64% rename from config/secrets.yml rename to viscoll-api/config/secrets.yml index 34116365..e0f98441 100644 --- a/config/secrets.yml +++ b/viscoll-api/config/secrets.yml @@ -11,10 +11,12 @@ # if you're sharing your code publicly. development: - secret_key_base: fe3e18f8b5a59698d997c8e5dd20303ed84a86ed28f4a94469adcebf8e2c4905e635761aa743ce87d4b88275ad44c696ee83cc2cd8859f0cdc393c494caf092c - + secret_key_base: 98eb3bcbe406c141ad93c58e5f5ff08ab7348c82d688b78ee1fb1a30559d7104081e0dd9bf97c8e080a54f1d408f7f9d22710439c44cbbddc332861994b1c531 + admin_email: 'smtp://localhost:1025' + api_url: 'http://localhost:3001' + test: - secret_key_base: 3cf10572eca25c5e5785dd56e638a47d51523510d298d839023940487dc82982fc798add37eeb1b1abee64d4c159deecdb7f30e0bc1b812d2af264bb94b1d582 + secret_key_base: a8986cd44e89b6547fe0c8ebd320706dc2dbd6aa617e42c5625b9420fa8ab8347beeedb0690138e178e79583b0f7c71b45fac99409d96653030c6a94c14d9d9d # Do not keep production secrets in the repository, # instead read values from the environment. diff --git a/viscoll-api/config/shrine.rb b/viscoll-api/config/shrine.rb new file mode 100644 index 00000000..b7aafb40 --- /dev/null +++ b/viscoll-api/config/shrine.rb @@ -0,0 +1,7 @@ +require "shrine" +require "shrine/storage/file_system" +Shrine.storages = { + cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary + store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"), # permanent +} +Shrine.plugin :data_uri diff --git a/config/spring.rb b/viscoll-api/config/spring.rb similarity index 100% rename from config/spring.rb rename to viscoll-api/config/spring.rb diff --git a/db/seeds.rb b/viscoll-api/db/seeds.rb similarity index 100% rename from db/seeds.rb rename to viscoll-api/db/seeds.rb diff --git a/app/controllers/concerns/.keep b/viscoll-api/lib/tasks/.keep similarity index 100% rename from app/controllers/concerns/.keep rename to viscoll-api/lib/tasks/.keep diff --git a/app/models/concerns/.keep b/viscoll-api/log/.keep similarity index 100% rename from app/models/concerns/.keep rename to viscoll-api/log/.keep diff --git a/viscoll-api/public/docs/index.html b/viscoll-api/public/docs/index.html new file mode 100644 index 00000000..a619f74b --- /dev/null +++ b/viscoll-api/public/docs/index.html @@ -0,0 +1,60 @@ + + + + + + API Docs + + + + + + + + + +
+ + + + + + + diff --git a/viscoll-api/public/docs/viscoll_api.yaml b/viscoll-api/public/docs/viscoll_api.yaml new file mode 100644 index 00000000..25eb74cc --- /dev/null +++ b/viscoll-api/public/docs/viscoll_api.yaml @@ -0,0 +1,2910 @@ +swagger: '2.0' +info: + description: Documentation of all endpoints + version: 1.0.0 + title: VisColl API + + +# tags are used for organizing operations +tags: +- name: Authentication + description: JWT based authentication +- name: Users + description: Operations on User model +- name: Projects + description: Operations on Project model +- name: Groups + description: Operations on Group model +- name: Leafs + description: Operations on Leaf model +- name: Sides + description: Operations on Side model +- name: Notes + description: Operations on Note model + +paths: + /session: + post: + tags: + - Authentication + summary: creates a session for a user + operationId: loginUser + description: | + By passing in the appropriate options, you can login a user and create a session + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: session + required: true + description: session object to create + schema: + $ref: '#/definitions/UserLoginParams' + responses: + 200: + description: user session successfully created + schema: + $ref: '#/definitions/UserLoginSuccess' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/UserLoginError' + delete: + tags: + - Authentication + summary: deletes the session for a user + operationId: logoutUser + description: | + By passing in the appropriate options, you can logout a user and delete the session + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: Authentication token + responses: + 204: + description: user session successfully deleted + 401: + description: Unauthorized Action + 422: + description: bad token header + schema: + $ref: '#/definitions/UserLogoutError' + /registration: + post: + tags: + - Authentication + summary: creates a new user + operationId: addUser + description: | + By passing in the appropriate options, you can add a new user to the database + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: user + required: true + description: user object to create + schema: + $ref: '#/definitions/UserRegisterParams' + responses: + 201: + description: user object successfully created and confirmation email sent to activate + schema: + $ref: '#/definitions/UserRegisterSuccess' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/UserRegisterError' + /confirmation: + put: + tags: + - Authentication + summary: confirms a user + operationId: confirmUser + description: | + By passing in the appropriate options, you can confirm a new user + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: token + required: true + description: confirmation token sent by email + schema: + $ref: '#/definitions/UserConfirmParams' + responses: + 204: + description: user successfully confirmed + 422: + description: bad input parameter + schema: + $ref: '#/definitions/UserConfirmError' + /password: + post: + tags: + - Authentication + summary: sends email to reset password + operationId: resetPasswordRequest + description: | + By passing in the appropriate options, you can request an email for password reset + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: password + required: true + description: email address to send password reset link + schema: + $ref: '#/definitions/UserPasswordResetRequestParams' + responses: + 204: + description: email was sent with password reset link + 422: + description: bad input parameter + schema: + $ref: '#/definitions/UserPasswordResetRequestError' + put: + tags: + - Authentication + summary: resets the user's password + operationId: resetPassword + description: | + By passing in the appropriate options, you can reset the password of the user + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: passwordReset + required: true + description: reset password token and new password + schema: + $ref: '#/definitions/UserPasswordResetParams' + responses: + 204: + description: password was successfully reset + 422: + description: bad input parameter + schema: + $ref: '#/definitions/UserPasswordResetError' + /users/{userID}: + get: + tags: + - Users + summary: gets information about a user + operationId: getUser + description: | + By passing in the appropriate options, you can view the user's information + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: userID + type: string + required: true + description: ID of the user + responses: + 200: + description: successfully retrieved the user's information + schema: + $ref: '#/definitions/UserResponseSimple' + 404: + description: user not found with id userID + schema: + type: object + properties: + error: + type: string + example: user not found with id userID + 401: + description: Unauthorized Action + 400: + description: Bad request due to token authorization + schema: + $ref: '#/definitions/TokenError' + delete: + tags: + - Users + summary: deletes a user + operationId: deleteUser + description: | + By passing in the appropriate options, you can delete a user + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: userID + type: string + required: true + description: ID of the user + responses: + 204: + description: successfully deleted the user + 401: + description: Unauthorized Action + 404: + description: user with userID not found + schema: + type: object + properties: + error: + type: string + example: user not found with id 5951303fc9bf3c7b9a573a3f + put: + tags: + - Users + summary: updates information about a user + operationId: updateUser + description: | + By passing in the appropriate options, you can update the user's information + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: userID + type: string + required: true + description: ID of the user + - in: body + name: user + required: true + description: | + Passing a new email address will invoke a confirmation mail sent which needs to be activated. + schema: + $ref: '#/definitions/UserUpdateParams' + responses: + 200: + description: successfully updated the user's information + schema: + $ref: '#/definitions/UserResponseSimple' + 401: + description: Unauthorized Action + 422: + description: bad input parameter + schema: + $ref: '#/definitions/UserUpdateError' + 404: + description: user not found with id userID + schema: + type: object + properties: + error: + type: string + example: user not found with id userID + 400: + description: Bad request due to token authorization + schema: + $ref: '#/definitions/TokenError' + /projects: + post: + tags: + - Projects + summary: creates a new project + operationId: createProject + description: | + By passing in the appropriate options, you can create a new project + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: project + required: true + description: project, manuscript and groups information + schema: + $ref: '#/definitions/ProjectCreateParams' + responses: + 200: + description: successfully created the project + schema: + $ref: '#/definitions/ProjectResponseSimple' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/ProjectCreateError' + 401: + description: Unauthorized Action + get: + tags: + - Projects + summary: gets list of all user projects + operationId: getProjects + description: | + By passing in the appropriate options, you can view all projects of the current user + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + responses: + 200: + description: successfully retrieved the user's projects + schema: + type: array + items: + $ref: '#/definitions/ProjectResponseSimple' + 401: + description: Unauthorized Action + /projects/{projectID}: + get: + tags: + - Projects + summary: gets information about a project + operationId: getProject + description: | + By passing in the appropriate options, you can view the project's information + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: projectID + type: string + required: true + description: ID of the project + responses: + 200: + description: successfully retrieved the project's information + schema: + $ref: '#/definitions/ProjectResponseFull' + 404: + description: project not found with id sad84d709c9bf3c1f76fd3fb + schema: + type: object + properties: + error: + type: string + example: project not found + 401: + description: Unauthorized Action + put: + tags: + - Projects + summary: creates a new project + operationId: updateProject + description: | + By passing in the appropriate options, you can update a project + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: projectID + type: string + required: true + description: ID of the project + - in: body + name: project + required: true + description: project and manuscript information + schema: + $ref: '#/definitions/ProjectUpdateParams' + responses: + 200: + description: successfully created the project + schema: + $ref: '#/definitions/ProjectResponseSimple' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/ProjectUpdateError' + 404: + description: project not found with id sad84d709c9bf3c1f76fd3fb + schema: + type: object + properties: + error: + type: string + example: project not found + 401: + description: Unauthorized Action + delete: + tags: + - Projects + summary: deletes a project + operationId: deleteProject + description: | + By passing in the appropriate options, you can delete a project + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: projectID + type: string + required: true + description: ID of the project + responses: + 204: + description: successfully deleted the project + 401: + description: Unauthorized Action + 404: + description: project not found with id sad84d709c9bf3c1f76fd3fb + schema: + type: object + properties: + error: + type: string + example: project not found + /projects/{projectID}/filter: + get: + tags: + - Projects + summary: filter the project + operationId: filterProject + description: | + By passing in the appropriate options, you can filter objects from the project + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: projectID + type: string + required: true + description: ID of the project + - in: body + name: filter + required: true + description: filter information + schema: + $ref: '#/definitions/ProjectFilterParams' + responses: + 200: + description: successfully filtered the project's information + schema: + $ref: '#/definitions/ProjectFilterResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/ProjectFilterError' + 404: + description: project not found with id sad84d709c9bf3c1f76fd3fb + schema: + type: object + properties: + error: + type: string + example: project not found + 401: + description: Unauthorized Action + /projects/{projectID}/notes: + get: + tags: + - Projects + summary: gets all notes in a project + operationId: getNotes + description: | + By passing in the appropriate options, you can view all notes in a project + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: projectID + type: string + required: true + description: ID of the project + responses: + 200: + description: successfully retrieved all notes for the project + schema: + $ref: '#/definitions/NotesFullResponse' + 401: + description: Unauthorized Action + 404: + description: project not found with id sad84d709c9bf3c1f76fd3fb + schema: + type: object + properties: + error: + type: string + example: project not found with id sad84d709c9bf3c1f76fd3fb + /projects/{projectID}/children: + get: + tags: + - Projects + summary: gets all children objects of a project + operationId: getChildren + description: | + By passing in the appropriate options, you can view all children objects of a project + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: projectID + type: string + required: true + description: ID of the project + responses: + 200: + description: successfully retrieved all children objects of the project + schema: + $ref: '#/definitions/ProjectChildrenResponse' + 401: + description: Unauthorized Action + 404: + description: project not found with id sad84d709c9bf3c1f76fd3fb + schema: + type: object + properties: + error: + type: string + example: project not found with id sad84d709c9bf3c1f76fd3fb + + /groups: + post: + tags: + - Groups + summary: creates a new group + operationId: createGroup + description: | + By passing in the appropriate options, you can create a new group + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: group + required: true + description: group information + schema: + $ref: '#/definitions/GroupCreateParams' + responses: + 200: + description: successfully created the group + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/GroupCreateError' + 401: + description: Unauthorized Action + put: + tags: + - Groups + summary: updates list of groups + operationId: updateGroups + description: | + By passing in the appropriate options, you can update a list of groups + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: groups + required: true + description: groups information + schema: + $ref: '#/definitions/GroupUpdateMultipleParams' + responses: + 200: + description: successfully updated the groups + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/GroupUpdateMultipleError' + 401: + description: Unauthorized Action + delete: + tags: + - Groups + summary: deletes list of groups + operationId: deletegroups + description: | + By passing in the appropriate options, you can delete the given groups + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: groups + required: true + description: groups information + schema: + $ref: '#/definitions/GroupDeleteMultipleParams' + responses: + 200: + description: successfully updated the groups + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/GroupDeleteMultipleError' + 401: + description: Unauthorized Action + 404: + description: group with groupID not found + schema: + type: object + properties: + error: + type: string + example: group not found + /groups/{groupID}: + put: + tags: + - Groups + summary: updates a group + operationId: updateGroup + description: | + By passing in the appropriate options, you can update a group + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: groupID + type: string + required: true + description: ID of the group + - in: body + name: group + required: true + description: group information + schema: + $ref: '#/definitions/GroupUpdateParams' + responses: + 200: + description: successfully updated the group + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/GroupUpdateError' + 401: + description: Unauthorized Action + 404: + description: group with groupID not found + schema: + type: object + properties: + error: + type: string + example: group not found + delete: + tags: + - Groups + summary: deletes a group + operationId: deleteGroup + description: | + By passing in the appropriate options, you can delete a group + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: groupID + type: string + required: true + description: ID of the group + responses: + 204: + description: successfully deleted the group + schema: + $ref: '#/definitions/ProjectResponseFull' + 401: + description: Unauthorized Action + 404: + description: group with groupID not found + schema: + type: object + properties: + error: + type: string + example: group not found + + + + /leafs: + post: + tags: + - Leafs + summary: creates a new leaf + operationId: createLeaf + description: | + By passing in the appropriate options, you can create a new leaf + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: leaf + required: true + description: leaf information + schema: + $ref: '#/definitions/LeafCreateParams' + responses: + 200: + description: successfully created the leaf + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/LeafCreateError' + 401: + description: Unauthorized Action + put: + tags: + - Leafs + summary: updates list of leaves + operationId: updateLeafs + description: | + By passing in the appropriate options, you can update a list of leaves + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: leafs + required: true + description: leafs information + schema: + $ref: '#/definitions/LeafUpdateMultipleParams' + responses: + 200: + description: successfully updated the leafs + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/LeafUpdateMultipleError' + 401: + description: Unauthorized Action + delete: + tags: + - Leafs + summary: deletes list of leaves + operationId: deleteLeafs + description: | + By passing in the appropriate options, you can delete the give leaevs + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: leafs + required: true + description: leafs information + schema: + $ref: '#/definitions/LeafDeleteMultipleParams' + responses: + 200: + description: successfully updated the leaf + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/LeafDeleteMultipleError' + 401: + description: Unauthorized Action + 404: + description: leaf with leafID not found + schema: + type: object + properties: + error: + type: string + example: leaf not found + /leafs/{leafID}: + put: + tags: + - Leafs + summary: updates a leaf + operationId: updateLeaf + description: | + By passing in the appropriate options, you can update a leaf + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: leafID + type: string + required: true + description: ID of the leaf + - in: body + name: leaf + required: true + description: leaf information + schema: + $ref: '#/definitions/LeafUpdateParams' + responses: + 200: + description: successfully updated the leaf + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/LeafUpdateError' + 401: + description: Unauthorized Action + 404: + description: leaf with leafID not found + schema: + type: object + properties: + error: + type: string + example: leaf not found + delete: + tags: + - Leafs + summary: deletes a leaf + operationId: deleteLeaf + description: | + By passing in the appropriate options, you can delete a leaf + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: leafID + type: string + required: true + description: ID of the leaf + responses: + 204: + description: successfully deleted the leaf + schema: + $ref: '#/definitions/ProjectResponseFull' + 401: + description: Unauthorized Action + 404: + description: leaf with leafID not found + schema: + type: object + properties: + error: + type: string + example: leaf not found + + + /sides: + put: + tags: + - Sides + summary: updates list of sides + operationId: updateSides + description: | + By passing in the appropriate options, you can update a list of sides + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: sides + required: true + description: sides information + schema: + $ref: '#/definitions/SideUpdateMultipleParams' + responses: + 200: + description: successfully updated the sides + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/SideUpdateMultipleError' + 401: + description: Unauthorized Action + /sides/{sideID}: + put: + tags: + - Sides + summary: updates a side + operationId: updateSide + description: | + By passing in the appropriate options, you can update a side + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: sideID + type: string + required: true + description: ID of the side + - in: body + name: side + required: true + description: side information + schema: + $ref: '#/definitions/SideUpdateParams' + responses: + 200: + description: successfully updated the side + schema: + $ref: '#/definitions/ProjectResponseFull' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/SideUpdateError' + 401: + description: Unauthorized Action + 404: + description: side with sideID not found + schema: + type: object + properties: + error: + type: string + example: side not found + + /notes: + post: + tags: + - Notes + summary: creates a new note + operationId: createNote + description: | + By passing in the appropriate options, you can create a new note + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: note + required: true + description: note information + schema: + $ref: '#/definitions/NoteCreateParams' + responses: + 200: + description: successfully created the note + schema: + $ref: '#/definitions/NotesFullResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteCreateError' + 401: + description: Unauthorized Action + + + + /notes/{noteID}: + put: + tags: + - Notes + summary: updates a notes + operationId: updateNote + description: | + By passing in the appropriate options, you can update a note + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: noteID + type: string + required: true + description: ID of the note + - in: body + name: note + required: true + description: note information + schema: + $ref: '#/definitions/NoteUpdateParams' + responses: + 200: + description: successfully updated the leaf + schema: + $ref: '#/definitions/NotesFullResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteCreateError' + 401: + description: Unauthorized Action + 404: + description: note with noteID not found + schema: + type: object + properties: + error: + type: string + example: note not found + delete: + tags: + - Notes + summary: deletes a note + operationId: deleteNote + description: | + By passing in the appropriate options, you can delete a note + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: noteID + type: string + required: true + description: ID of the note + responses: + 204: + description: successfully deleted the note + schema: + $ref: '#/definitions/NotesFullResponse' + 401: + description: Unauthorized Action + 404: + description: note with noteID not found + schema: + type: object + properties: + error: + type: string + example: note not found + /notes/{noteID}/link: + put: + tags: + - Notes + summary: links a note to the given objects + operationId: linkNote + description: | + By passing in the appropriate options, you can link a note to objects + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: noteID + type: string + required: true + description: ID of the note + - in: body + name: note + required: true + description: note information + schema: + $ref: '#/definitions/NoteLinkParams' + responses: + 200: + description: successfully linked the note + schema: + $ref: '#/definitions/NotesFullResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteLinkError' + 401: + description: Unauthorized Action + 404: + description: note with noteID not found + schema: + type: object + properties: + error: + type: string + example: note not found + /notes/{noteID}/unlink: + put: + tags: + - Notes + summary: unlinks a note from the given objects + operationId: unlinkNote + description: | + By passing in the appropriate options, you can unlink a note from objects + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: path + name: noteID + type: string + required: true + description: ID of the note + - in: body + name: note + required: true + description: note information + schema: + $ref: '#/definitions/NoteLinkParams' + responses: + 200: + description: successfully unlinked the note + schema: + $ref: '#/definitions/NotesFullResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteLinkError' + 401: + description: Unauthorized Action + 404: + description: note with noteID not found + schema: + type: object + properties: + error: + type: string + example: note not found + /notes/type: + post: + tags: + - Notes + summary: creates a note type + operationId: createNoteType + description: | + By passing in the appropriate options, you can create a note type + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: noteType + required: true + description: note information + schema: + $ref: '#/definitions/NoteTypeCreateParams' + responses: + 200: + description: successfully created the note type + schema: + $ref: '#/definitions/NoteTypeResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteTypeCreateError' + 401: + description: Unauthorized Action + put: + tags: + - Notes + summary: updates a note type + operationId: updateNoteType + description: | + By passing in the appropriate options, you can update a note type + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: noteType + required: true + description: note information + schema: + $ref: '#/definitions/NoteTypeUpdateParams' + responses: + 200: + description: successfully updated the note type + schema: + $ref: '#/definitions/NoteTypeResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteTypeUpdateError' + 401: + description: Unauthorized Action + delete: + tags: + - Notes + summary: deletes a note type + operationId: deleteNoteType + description: | + By passing in the appropriate options, you can delete a note type + - application/json + produces: + - application/json + parameters: + - in: header + name: Authorization + type: string + required: true + description: authentication token + - in: body + name: noteType + required: true + description: note information + schema: + $ref: '#/definitions/NoteTypeCreateParams' + responses: + 200: + description: successfully deleted the note type + schema: + $ref: '#/definitions/NoteTypeResponse' + 422: + description: bad input parameter + schema: + $ref: '#/definitions/NoteTypeDeleteError' + 401: + description: Unauthorized Action + + + + +definitions: + UserRegisterParams: + type: object + properties: + user: + type: object + required: + - email + - password + properties: + email: + type: string + example: example@mail.com + password: + type: string + example: secret123 + name: + type: string + example: John + UserRegisterSuccess: + type: object + properties: + user: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + email: + type: string + example: example@mail.com + name: + type: string + example: John + password_digest: + type: string + format: password encrypt + example: $2a$10$/CY5b5qDleekbFbMVIFTZ.61VIjAsNGaOB5vQ4zrSWwyHvVL.G/P6 + confirmation_token: + type: string + example: LTn4sV79adhRDyc5k1r3yaQk + confirmation_sent_at: + type: string + format: date-time + example: "2017-07-12T14:04:34.799Z" + UserRegisterError: + type: object + properties: + errors: + type: object + properties: + email: + type: array + items: + type: string + example: [can't be blank, is not an email, is already taken] + password: + type: array + items: + type: string + example: [can't be blank] + UserConfirmParams: + type: object + required: + - confirmation_token + properties: + confirmation_token: + type: string + example: 5951303fc9bf3c7b9a573a3f + UserConfirmError: + type: object + properties: + errors: + type: object + properties: + confirmation_token: + type: array + items: + type: string + example: [not found] + UserLoginParams: + type: object + properties: + session: + type: object + required: + - email + - password + properties: + email: + type: string + example: example@mail.com + password: + type: string + example: secret123 + UserLoginSuccess: + type: object + properties: + session: + type: object + properties: + jwt: + type: string + example: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoX3Rva2VuIjoiTjVGZkN + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + email: + type: string + example: example@mail.com + name: + type: string + example: John + lastLoggedIn: + type: string + format: date-time + example: 2017-07-12T14:04:34.799Z + projects: + type: array + items: + $ref: '#/definitions/ProjectResponseSimple' + UserLoginError: + type: object + properties: + errors: + type: object + properties: + session: + type: array + items: + type: string + example: [invalid email / password, unconfirmed email] + UserLogoutError: + type: object + properties: + error: + type: string + example: Authorization Header Signature verification raised, Not enough or too many segments + UserPasswordResetRequestParams: + type: object + properties: + password: + type: object + required: + - email + properties: + email: + type: string + example: example@mail.com + UserPasswordResetRequestError: + type: object + properties: + errors: + type: object + properties: + email: + type: array + items: + type: string + example: [unconfirmed email, not found] + UserPasswordResetParams: + type: object + required: + - reset_password_token + properties: + reset_password_token: + type: string + example: 5951303fc9bf3c7b9a573a3f + password: + type: object + required: + - password + - password_confirmation + properties: + password: + type: string + example: secret123 + password_confirmation: + type: string + example: secret123 + UserPasswordResetError: + type: object + properties: + errors: + type: object + properties: + reset_password_token: + type: array + items: + type: string + example: [not found, has expired please request a new one] + password: + type: array + items: + type: string + example: [blank] + password_confirmation: + type: array + items: + type: string + example: [doesn't match Password] + UserUpdateParams: + type: object + properties: + user: + type: object + properties: + name: + type: string + example: John + email: + type: string + example: example@mail.com + current_password: + type: string + example: secret123 + password: + type: string + example: new_secret123 + UserUpdateError: + type: object + properties: + email: + type: array + items: + type: string + example: [is already taken, is not at email] + current_password: + type: array + items: + type: string + example: [invalid, blank, nil] + password: + type: array + items: + type: string + example: [invalid, blank, nil] + UserResponseSimple: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + email: + type: string + example: example@mail.com + name: + type: string + example: John + projects: + type: array + items: + $ref: '#/definitions/ProjectResponseSimple' + TokenError: + type: object + properties: + error: + type: string + example: Authorization Token Signature verification raised, Nil JSON web token, Not enough or too many segments + ProjectResponseSimple: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + title: + type: string + example: My first project + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + manuscript: + $ref: '#/definitions/ManuscriptResponseSimple' + ManuscriptResponseSimple: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + shelfmark: + type: string + example: MSS 123 + uri: + type: string + format: url + example: some iiif manifest url + date: + type: string + example: 18th century + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + ProjectCreateParams: + type: object + properties: + project: + type: object + properties: + title: + type: string + example: My first project + manuscript: + type: object + properties: + shelfmark: + type: string + example: MSS 123 + uri: + type: string + format: url + example: some iiif manifest url + date: + type: string + example: 18th century + groups: + type: array + items: + type: object + properties: + number: + type: integer + example: 1 + description: the group order amoung other groups + leaves: + type: integer + example: 4 + description: number of leaves in this group + conjoin: + type: boolean + example: true + description: whether to auto-conjoin or not + oddLeaf: + type: integer + example: 3 + description: if auto-conjoining odd number of leaves, the leaf number to exclude + ProjectCreateError: + type: object + properties: + project: + type: object + properties: + title: + type: array + items: + type: string + example: [Project title is required, Project title should be unique] + manuscript: + type: object + properties: + shelfmark: + type: array + items: + type: string + example: [Manuscript shelfmark is required] + groups: + type: array + items: + type: object + properties: + groupID: + type: integer + example: 1 + description: the group number that has errors + number: + type: array + items: + type: string + example: [should be an Integer, should be greater than 0, should be equal to 1] + leaves: + type: array + items: + type: string + example: [should be an Integer, should be greater than 0] + conjoin: + type: array + items: + type: string + example: [should be a Boolean] + oddLeaf: + type: array + items: + type: string + example: [should be an Integer, should be greater than 0, cannot be greater than leaves] + + ProjectUpdateParams: + type: object + properties: + project: + type: object + properties: + title: + type: string + example: My first project + manuscript: + type: object + properties: + shelfmark: + type: string + example: MSS 123 + uri: + type: string + format: url + example: some iiif manifest url + date: + type: string + example: 18th century + + ProjectUpdateError: + type: object + properties: + project: + type: object + properties: + title: + type: array + items: + type: string + example: [Project title is required, Project title should be unique] + manuscript: + type: object + properties: + shelfmark: + type: array + items: + type: string + example: [Manuscript shelfmark is required] + + ProjectResponseFull: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + title: + type: string + example: My first project + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + noteTypes: + type: array + items: + type: string + example: [Unknown, Ink, Hand] + notes: + $ref: '#/definitions/NotesFullResponse' + manuscript: + $ref: '#/definitions/ManuscriptResponseFull' + ManuscriptResponseFull: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + shelfmark: + type: string + example: MSS 154 + uri: + type: string + format: url + example: http://universalviewer.azurewebsites.net/manifests.json + date: + type: string + example: 16th century + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + numberOfLeaves: + type: integer + example: 6 + numberOfGroups: + type: integer + example: 2 + attachedToLeafs: + type: array + items: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + order: + type: string + example: None or Binding + groups: + type: array + items: + $ref: '#definitions/GroupFullResponse' + GroupFullResponse: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + order: + type: integer + example: 1 + description: global order within the project + title: + type: string + example: Deafult + type: + type: string + example: Quire/Booklet + member_type: + type: string + example: Group + member_order: + type: integer + example: 1 + nestLevel: + type: integer + example: 0 + description: nested level within groups + members: + type: array + items: + $ref: '#definitions/MemberFullResponse' + MemberFullResponse: + type: object + properties: + member_type: + type: string + example: Group/Leaf + member_order: + type: integer + example: 1 + description: local order within the group + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + order: + type: integer + example: 1 + description: global order within the project + nestLevel: + type: integer + example: 0 + description: nested level within groups + conjoined_leaf_order: + type: integer + example: 3 + description: leaf order of this leaf's conjoined member + material: + type: string + example: Parchment + type: + type: string + example: Added + attachment_method: + type: string + example: Glued + conjoined_to: + type: string + example: 595e59f3c9bf3c6760f2e328 + description: leafID of the conjoined leaf + attached_to: + type: array + items: + type: string + example: 595e59f3c9bf3c6760f2e328 + description: leafIDs of the attached_to leafs + stub: + type: string + example: Original + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + parent: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + title: + type: string + example: Deafult + order: + type: integer + example: 1 + description: global order within the project + type: + type: string + example: Quire/Booklet + sides: + type: array + items: + $ref: '#definitions/SideFullResponse' + SideFullResponse: + type: object + properties: + id: + type: string + format: uuid + example: 5951303fc9bf3c7b9a573a3f + order: + type: integer + example: 0 + description: either 0 or 1, Recto or Verso + folio_number: + type: string + example: 2v + texture: + type: string + example: Hair + uri: + type: string + format: url + example: some iiif image url + script_direction: + type: string + example: Left + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + LeafCreateParams: + type: object + properties: + leaf: + type: object + required: + - manuscript_id + properties: + manuscript_id: + type: string + example: 5951303fc9bf3c7b9a573a3f + order: + type: integer + example: 2 + material: + type: string + example: Parchment + type: + type: string + example: Added + attachment_method: + type: string + example: Glued + conjoined_to: + type: string + example: 5951303fc9bf3c7b9a573a3f + description: leafID of the conjoined leaf + atached_to: + type: array + items: + type: string + example: 5951303fc9bf3c7b9a573a3f + description: leafIDs of the attached_to leafs + stub: + type: string + example: Original + additional: + type: object + required: + - groupID + - memberOrder + - noOfLeafs + - conjoin + - oddMemberLeftOut + - noOfRepeats + properties: + groupID: + type: string + example: 5951303fc9bf3c7b9a573a3f + description: groupID of the group this leaf belongs to + memberOrder: + type: integer + example: 2 + description: the local order within this group + noOfLeafs: + type: integer + example: 5 + description: total number of leaves to add + conjoin: + type: boolean + example: true + description: whether to auto-conjoin or not + oddMemberLeftOut: + type: integer + example: 2 + description: if auto-conjoining odd number of leaves, the leaf number to exclude + noOfRepeats: + type: integer + example: 2 + description: if auto-conjoining, the number of times to repeat this action + + + GroupCreateParams: + type: object + properties: + group: + type: object + properties: + manuscript_id: + type: string + example: 5951303fc9bf3c7b9a573a3f + type: + type: string + example: Quire/Booklet + title: + type: string + example: Some title + order: + type: integer + example: 2 + additional: + type: object + properties: + parentGroupID: + type: string + example: 5951303fc9bf3c7b9a573a3f + description: groupID of the group this group belongs to. can be null if this is a root group. + memberOrder: + type: integer + example: 2 + description: the local order of this group within the manuscript + noOfGroups: + type: integer + example: 5 + description: total number of groups to add + noOfLeafs: + type: integer + example: 5 + description: total number of leaves to add in the group + conjoin: + type: boolean + example: true + description: whether to auto-conjoin or not + oddMemberLeftOut: + type: integer + example: 2 + description: if auto-conjoining odd number of leaves, the leaf number to exclude + GroupCreateError: + type: object + properties: + group: + type: object + properties: + manuscript_id: + type: array + items: + type: string + example: [is required, should be a String, manuscript not found] + type: + type: array + items: + type: string + example: [is required, should be either Quire or Booklet] + order: + type: array + items: + type: string + example: [is required, should be an Integer] + additional: + type: object + properties: + parentGroupID: + type: array + items: + type: string + example: [is required, should be a String, Group with groupID does not exist] + memberOrder: + type: array + items: + type: integer + example: [is required, should be an Integer, should be greater than 0] + noOfGroups: + type: array + items: + type: integer + example: [is required, should be an Integer, should be greater than 0 or less than 999] + noOfLeafs: + type: array + items: + type: integer + example: [is required, should be an Integer, should be greater than 0 or less than 999] + conjoin: + type: array + items: + type: boolean + example: [is required, should be a Boolean, should be false if noOfLeafs is 1] + oddMemberLeftOut: + type: array + items: + type: integer + example: [is required, should be an Integer, should be greater than 0 and less than noOfLeafs, should only be 0 if noOfLeafs is even] + + GroupUpdateParams: + type: object + properties: + group: + type: object + properties: + type: + type: string + example: Quire + title: + type: string + example: Some title + GroupUpdateError: + type: object + properties: + group: + type: object + properties: + type: + type: array + items: + type: string + example: [should be either Quire or Booklet] + GroupUpdateMultipleParams: + type: object + properties: + groups: + type: array + items: + type: object + properties: + id: + type: string + example: 5951303fc9bf3c7b9a573a3f + attributes: + type: object + properties: + type: + type: string + example: Quire/Booklet + title: + type: string + example: Some title + + GroupUpdateMultipleError: + type: object + properties: + groups: + type: array + items: + type: object + properties: + id: + type: string + example: group not found with id 5971005ec9bf3c32462bdd37s + attributes: + type: object + properties: + type: + type: string + example: should be either Quire or Booklet + GroupDeleteMultipleParams: + type: object + properties: + groups: + type: array + items: + type: string + example: 5951303fc9bf3c7b9a573a3f + GroupDeleteMultipleError: + type: object + properties: + groups: + type: array + items: + type: string + example: [group not found with id 5971005ec9bf3c32462bdd37s] + + + LeafCreateError: + type: object + properties: + leaf: + type: object + properties: + manuscript_id: + type: array + items: + type: string + example: [is required, should be a String, manuscript not found] + order: + type: array + items: + type: integer + example: [is required, should be an Integer, should be greater than 0] + additional: + type: object + properties: + groupID: + type: array + items: + type: string + example: [is required, should be a String, Group with groupID does not have manuscript_id as a member, group not found] + memberOrder: + type: array + items: + type: string + example: [is required, should be an Integer, should be greater than 0] + noOfLeafs: + type: array + items: + type: string + example: [is required, should be an Integer, should be greater than 0 or less than 999] + conjoin: + type: array + items: + type: string + example: [is required, should be a Boolean, should be false if noOfLeafs is 1] + oddMemberLeftOut: + type: array + items: + type: string + example: [is required, should be an Integer, should be greater than 0 and less than noOfLeafs, should only be 0 if noOfLeafs is even] + noOfRepeats: + type: array + items: + type: string + example: [is required, should be an Integer, should only be 1 if conjoin is false, should be greater than 1 or less than 99] + LeafUpdateParams: + type: object + properties: + leaf: + type: object + properties: + order: + type: integer + example: 2 + material: + type: string + example: Parchment + type: + type: string + example: Added + attachment_method: + type: string + example: Glued + conjoined_to: + type: string + example: 5951303fc9bf3c7b9a573a3f + description: leafID of the conjoined leaf + attached_to: + type: object + properties: + aboveID: + type: string + example: 5951303fc9bf3c7b9a573a3f + aboveMethod: + type: string + example: Glued + belowID: + type: string + example: 5951303fc9bf3c7b9a573a3f + belowMethod: + type: string + example: Sewn + stub: + type: string + example: Original + LeafUpdateError: + type: object + properties: + leaf: + type: object + properties: + order: + type: array + items: + type: integer + example: should be an Integer, should be greater than 0 + conjoined_to: + type: array + items: + type: integer + example: conjoined_to leaf does not exist + attached_to: + type: object + properties: + aboveID: + type: array + items: + type: string + example: [Missing parameter aboveID, Leaf not found with id 5951303fc9bf3c7b9a573a3f] + aboveMethod: + type: array + items: + type: string + example: [Should be one of Glued Sewn or Tacketed] + belowID: + type: array + items: + type: string + example: [Missing parameter belowID, Leaf not found with id 5951303fc9bf3c7b9a573a3f] + belowMethod: + type: array + items: + type: string + example: [Should be one of Glued Sewn or Tacketed] + LeafUpdateMultipleParams: + type: object + properties: + leafs: + type: array + items: + type: object + properties: + id: + type: string + example: 5951303fc9bf3c7b9a573a3f + attributes: + type: object + properties: + material: + type: string + example: Parchment + type: + type: string + example: Added + stub: + type: string + example: Original + LeafUpdateMultipleError: + type: object + properties: + leafs: + type: array + items: + type: object + properties: + id: + type: array + items: + type: string + example: leaf not found with id 5951303fc9bf3c7b9a573a3f + attributes: + type: object + properties: + material: + type: array + items: + type: string + example: [Invalid] + type: + type: array + items: + type: string + example: [Invalid] + stub: + type: array + items: + type: string + example: [Invalid] + LeafDeleteMultipleParams: + type: object + properties: + leafs: + type: array + items: + type: string + example: 5951303fc9bf3c7b9a573a3f + + LeafDeleteMultipleError: + type: object + properties: + leafs: + type: array + items: + type: string + example: leaf not found with id 5951303fc9bf3c7b9a573a3f + + + + SideUpdateParams: + type: object + properties: + side: + type: object + properties: + folio_number: + type: string + example: 1v + texture: + type: string + example: Paper + uri: + type: string + example: some IIIF image url + script_direction: + type: string + example: left + SideUpdateError: + type: object + properties: + side: + type: object + properties: + folio_number: + type: array + items: + type: string + example: [Invalid] + texture: + type: array + items: + type: string + example: [Invalid] + uri: + type: array + items: + type: string + example: [Invalid URL] + script_direction: + type: array + items: + type: string + example: [Invalid] + + SideUpdateMultipleParams: + type: object + properties: + sides: + type: array + items: + type: object + properties: + id: + type: string + example: 5951303fc9bf3c7b9a573a3f + attributes: + type: object + properties: + texture: + type: string + example: Paper + script_direction: + type: string + example: left + + SideUpdateMultipleError: + type: object + properties: + sides: + type: array + items: + type: object + properties: + id: + type: array + items: + type: string + example: side not found with id 5951303fc9bf3c7b9a573a3f + + NoteCreateParams: + type: object + properties: + note: + type: object + properties: + project_id: + type: string + example: 5951303fc9bf3c7b9a573a3f + title: + type: string + example: some title for note + type: + type: string + example: Ink + description: + type: string + example: blue ink + + NoteUpdateParams: + type: object + properties: + note: + type: object + properties: + title: + type: string + example: some title for note + type: + type: string + example: Ink + description: + type: string + example: blue ink + + NotesFullResponse: + type: array + items: + $ref: '#definitions/NoteFullResponse' + + + NoteFullResponse: + type: object + properties: + id: + type: string + example: 5951303fc9bf3c7b9a573a3f + title: + type: string + example: some title for note + type: + type: string + example: Ink + description: + type: string + example: blue ink + created_at: + type: string + example: "2017-07-12T14:04:34.799Z" + updated_at: + type: string + example: "2017-07-12T14:04:34.799Z" + objects: + type: object + properties: + Group: + type: object + properties: + 5951303fc9bf3c7b9a573a3f: + type: string + example: 1 + Leaf: + type: object + properties: + 5951303fc9bf3c7b9a573a3f: + type: string + example: 2 + Side: + type: object + properties: + 5951303fc9bf3c7b9a573a3f: + type: string + example: 1 + + NoteLinkParams: + type: object + properties: + objects: + type: array + items: + type: object + properties: + id: + type: string + example: 5951303fc9bf3c7b9a573a3f + type: + type: string + example: Group + + NoteCreateError: + type: object + properties: + title: + type: array + example: [Note title should be uniue] + type: + type: array + example: [Note type is required] + + NoteLinkError: + type: object + properties: + id: + type: string + example: Group object not found with id 5984d709c9bf3c1f76fd3fb + type: + type: string + example: object not found with type Groupssss + + NoteTypeCreateParams: + type: object + properties: + noteType: + type: object + properties: + project_id: + type: string + example: 5951303fc9bf3c7b9a573a3f + type: + type: string + example: Ink + + NoteTypeUpdateParams: + type: object + properties: + noteType: + type: object + properties: + project_id: + type: string + example: 5951303fc9bf3c7b9a573a3f + type: + type: string + example: Ink + old_type: + type: string + example: Inkss + + NoteTypeCreateError: + type: object + properties: + project_id: + type: string + example: project object not found with id 5984d709c9bf3c1f76fd3fb + type: + type: string + example: already exists in the project + + + NoteTypeUpdateError: + type: object + properties: + project_id: + type: string + example: project object not found with id 5984d709c9bf3c1f76fd3fb + type: + type: string + example: already exists in the project + old_type: + type: string + example: doesn't exist in the project + + + NoteTypeDeleteError: + type: object + properties: + project_id: + type: string + example: project object not found with id 5984d709c9bf3c1f76fd3fb + type: + type: string + example: doesn't exist in the project + + NoteTypeResponse: + type: object + properties: + noteTypes: + type: array + items: + type: string + example: [Ink, Hand] + + + ProjectFilterParams: + type: object + properties: + queries: + type: array + items: + type: object + properties: + type: + type: string + example: Leaf + attribute: + type: string + example: Material + condition: + type: string + example: equals + values: + type: array + example: [paper, parchment] + conjunction: + type: string + example: AND + + ProjectFilterResponse: + type: object + properties: + Groups: + type: array + items: + type: string + example: [5984d709c9bf3c1f76fd3fb, 5984d709c9bf3c1f76asdsadb, sad84d709c9bf3c1f76fd3fb] + Leafs: + type: array + items: + type: string + example: [5984d709c9bf3c1f76fd3fb, 5984d709c9bf3c1f76asdsadb, sad84d709c9bf3c1f76fd3fb] + Sides: + type: array + items: + type: string + example: [5984d709c9bf3c1f76fd3fb, 5984d709c9bf3c1f76asdsadb, sad84d709c9bf3c1f76fd3fb] + Notes: + type: array + items: + type: string + example: [5984d709c9bf3c1f76fd3fb, 5984d709c9bf3c1f76asdsadb, sad84d709c9bf3c1f76fd3fb] + GroupsOfLeafs: + type: array + items: + type: string + example: [5984d709c9bf3c1f76fd3fb, 5984d709c9bf3c1f76asdsadb, sad84d709c9bf3c1f76fd3fb] + + ProjectFilterError: + type: object + properties: + errors: + type: array + items: + type: object + properties: + type: + type: string + example: valid attributes for group are type, title + attribute: + type: string + example: valid attributes for leafare type, material, conjoined_to, attached_to, stub + condition: + type: string + example: valid conditions for leaf attribute are equals, not_equals + values: + type: string + example: filter value cannot be empty + conjunction: + type: string + example: conjunction should be one of AND, OR + + ProjectChildrenResponse: + type: object + properties: + groups: + type: array + items: + type: object + properties: + id: + type: string + example: 5984d709c9bf3c1f76fd3fb + order: + type: string + example: 2 + leafs: + type: array + items: + type: object + properties: + id: + type: string + example: 5984d709c9bf3c1f76fd3fb + order: + type: string + example: 2 + sides: + type: array + items: + type: object + properties: + id: + type: string + example: 5984d709c9bf3c1f76fd3fb + order: + type: string + example: 2 + + +basePath: /api + diff --git a/viscoll-api/public/viscoll-datamodel2.0.rng b/viscoll-api/public/viscoll-datamodel2.0.rng new file mode 100644 index 00000000..127f095a --- /dev/null +++ b/viscoll-api/public/viscoll-datamodel2.0.rng @@ -0,0 +1,390 @@ + + + + + + On viscoll document may contain multiple textblock elements + + Optional @url points to a record describing the textblock, e.g. a catalog + record + + + + + + + + Optional + @title provides the title of the textblock as defined by the people + or group doing the cataloging (viscoll does not define + title) + + + + + + + + + @date is + optional and is undefined. Could be linked to a controlled + vocabulary in tools (e.g., a list of centuries) + + + + + + origPlace is optional and is undefined. Could be linked to a + controlled vocabulary in tools + + + + + + + direction is required and the default value is l-r + + + l-r + r-l + + + + + + + refers to the format, usually of a printed book. Possible values are 2mo, 4mo, 8mo, 12mo, 16mo, 32mo, 64mo + + + 2mo + 4mo + 8mo + 12mo + 16mo + 24mo + + + + + + + + refers to the category list in the "Table of Fifteenth-Century Paper Flavors" for the Needham Calculator (http://www.needhamcalculator.net/) + + + imperial + super-royal + royal + super-median + median + super-chancery + chancery + half-median + + + + + + + The + textblock begins with a list of quires + + + + + + + + One leaf element to describe each leaf in the + textblock + + + + If a leaf is a stub, the value of @stub is "yes" - + otherwise @stub is not there + yes + + + + id + + + + + + + + + + + + + + + + + + + + + + + original + added + replaced + missing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + single only has the value of "yes" and is only used if the + leaf is a singleton + + + yes + + + + + + + + + + Indicates the leaf (recto of) which is the start of the first quire. + + + yes + + + + + + + + + + Indicates if the leaf includes a foldout. The @direction attribute specifies direction (out, up, or down). May be repeated if the leaf has multiple foldouts. The order of multiple foldouts must be the order in which the leaf is unfolded. + + + out + in + up + down + + + + + + + + + + defines the values allowed for identifying the methods by which one leaf is attached to another. For "sewn" if there is a @target identified, the two leaves are sewn together, if there is no @target identified it is sewn through the fold. Use "sewn" through the fold only when you see it (i.e., in the center of a quire) + + + + + + + + sewn + pasted + tipped + drummed + other + + + + + + + + + + + + + certainty + + + + + id + + + + + + + viscoll + element begins with an optional set of taxonomy definitions + + + taxonomy + if + the taxonomy is defined externally, e.g. the Getty Art & Architecture + Thesaurus, include a @ref pointing to it + + + + + + + id + + + + label + + + + + Any defined taxonomy must include at least one term. If the taxonomy is defined external to the viscoll document, the term must have a @ref pointing to the external definition + term + + + + + + + id + + + + + + + + + + mapping + + + map + + + + + + + + + + term + + + + + + + + + + + + + + + + values for @certainty: 1 = very certain, 2 = fairly certain, 3 = not certain + + 1 + 2 + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + subquires must point to a parent quire + + + + + + + id + + + + + + + diff --git a/viscoll-api/public/viscoll-datamodel2.rng b/viscoll-api/public/viscoll-datamodel2.rng new file mode 100644 index 00000000..4fd9ad77 --- /dev/null +++ b/viscoll-api/public/viscoll-datamodel2.rng @@ -0,0 +1,299 @@ + + + + + viscoll element begins with an optional set of taxonomy definitions + + + taxonomy + if the taxonomy is defined externally, e.g. the Getty Art & Architecture Thesaurus, include a @ref pointing to it + + + + + + + id + + + + + label + + + + + + term + + + + + + + id + + + + + + + + + + + + + Optional @url points to a record describing the manuscript, e.g. a catalog record + + + + + + + + Optional @title provides the title of the manuscript as defined by the people or group doing the cataloging (viscoll does not define title) + + + + + + + + + + @date is optional and is undefined. Could be linked to a controlled vocabulary in tools + + + + + + origPlace is optional and is undefined. Could be linked to a controlled vocabulary in tools + + + + + + + direction is required and the default value is l-r + + + l-r + r-l + + + + + + The manuscript begins with a list of quires + + + + + + + + One leaf element to describe each leaf in the manuscript + + + + If a leaf is a stub, the value of @stub is "yes" - otherwise @stub is not there + yes + + + + id + + + + + + + + + + + + + + + + + + + + + + + original + added + replaced + false + missing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + single only has the value of "yes" and is only used if the leaf is a singleton + + + yes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + certainty + + + + + id + + + + + + + + + + mapping + + + map + + + + + + + + + + term + + + + + + + + + + + + + + + + + + 1 + 2 + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id + + + + + + + diff --git a/viscoll-api/spec/factories/groups.rb b/viscoll-api/spec/factories/groups.rb new file mode 100644 index 00000000..8eed6302 --- /dev/null +++ b/viscoll-api/spec/factories/groups.rb @@ -0,0 +1,73 @@ +FactoryGirl.define do + sequence :quire_title do |n| + "Quire #{n}" + end + sequence :booklet_title do |n| + "Booklet #{n}" + end + sequence :group_title do |n| + "Group #{n}" + end + factory :group, class: Group do + transient do + members [] + end + after(:create) do |group, evaluator| + group.nestLevel ||= 1 + unless evaluator.members.blank? + newmembers = evaluator.members.each do |member| + if member.is_a?(Group) + member.nestLevel = group.nestLevel+1 + else + member.nestLevel = group.nestLevel + end + member.save + end + group.add_members(newmembers.collect { |member| member.id.to_s }, 1) + end + group.save + end + title { generate(:group_title) } + type "Quire" + end + + factory :quire, class: Group do + transient do + leafs 0 + conjoined true + leaf_properties { {} } + start_page 1 + end + after(:create) do |group, evaluator| + group.nestLevel ||= 1 + unless evaluator.leafs <= 0 + newleafprops = evaluator.leaf_properties.merge({ + project_id: group.project_id, + parentID: group.id.to_s, + nestLevel: group.nestLevel + }) + newleafs = evaluator.leafs.times.collect { |n| + FactoryGirl.build(:leaf, newleafprops.merge({ folio_number: evaluator.start_page+n })) + } + if evaluator.conjoined + evaluator.leafs.times.each do |n| + unless evaluator.leafs.odd? and n == evaluator.leafs >> 1 + conjoin_id = newleafs[-1-n].id.to_s + newleafs[n].conjoined_to = if conjoin_id[0..4] == 'Leaf_' then conjoin_id else "Leaf_#{conjoin_id}" end + end + newleafs[n].save + end + end + group.add_members(newleafs.collect { |newleaf| newleaf.id.to_s }, 1) + end + end + title { generate(:quire_title) } + type "Quire" + end + + factory :booklet, parent: :quire do + title { generate(:booklet_title) } + type "Booklet" + leafs 0 + end +end diff --git a/viscoll-api/spec/factories/images.rb b/viscoll-api/spec/factories/images.rb new file mode 100644 index 00000000..58238987 --- /dev/null +++ b/viscoll-api/spec/factories/images.rb @@ -0,0 +1,47 @@ +FactoryGirl.define do + sequence :image_filename do |n| + "Image #{n}" + end + + sequence :image_fileid do |n| + "#{n}" + end + + sequence :image_original_filename do |n| + "image_#{n}" + end + + factory :image do + filename { generate(:image_filename) } + + factory :pixel do + filename { 'pixel.png'} + fileID { 'pixel'} + metadata { { + "filename": "pixel.png", + "size": 20470, + "mime_type": "image/png" + } } + end + + factory :shiba_inu do + filename { 'shiba_inu.png'} + fileID { 'shiba_inu'} + metadata { { + "filename": "shiba_inu.png", + "size": 20470, + "mime_type": "image/png" + } } + end + + factory :viscoll_logo do + filename { 'viscoll_logo.png'} + fileID { 'viscoll_logo'} + metadata { { + "filename": "viscoll_logo.png", + "size": 20470, + "mime_type": "image/png" + } } + end + end +end diff --git a/viscoll-api/spec/factories/leafs.rb b/viscoll-api/spec/factories/leafs.rb new file mode 100644 index 00000000..c56245d9 --- /dev/null +++ b/viscoll-api/spec/factories/leafs.rb @@ -0,0 +1,20 @@ +include ActionDispatch::TestProcess +FactoryGirl.define do + factory :leaf do + transient { + folio_number nil + } + after(:create) do |leaf, evaluator| + unless evaluator.folio_number.blank? + Side.find(leaf.rectoID).update(folio_number: "#{evaluator.folio_number}R") + Side.find(leaf.versoID).update(folio_number: "#{evaluator.folio_number}V") + end + end + material "Paper" + type "Original" + + factory :parchment do + material "Parchment" + end + end +end diff --git a/viscoll-api/spec/factories/notes.rb b/viscoll-api/spec/factories/notes.rb new file mode 100644 index 00000000..2f5e6869 --- /dev/null +++ b/viscoll-api/spec/factories/notes.rb @@ -0,0 +1,34 @@ +FactoryGirl.define do + sequence :note_title do |n| + "Note #{n}" + end + sequence :note_text do |n| + "Blah #{n}" + end + + factory :note do + transient do + attachments [] + end + before(:build) do |note, evaluator| + myobjects = {Group: [], Leaf: [], Recto: [], Verso: []} + evaluator.attachments.each do |attachment| + if attachment.is_a? Group + myobjects[:Group] << attachment + elsif attachment.is_a? Leaf + myobjects[:Leaf] << attachment + elsif attachment.is_a? Side + if attachment.id.to_s[0..5] == 'Verso_' + myobjects[:Verso] << attachment + else + myobjects[:Recto] << attachment + end + else + raise Exception('Notes can only be attached to groups, leafs and sides') + end + end + end + title { generate(:note_title) } + type "Unknown" + end +end diff --git a/viscoll-api/spec/factories/projects.rb b/viscoll-api/spec/factories/projects.rb new file mode 100644 index 00000000..1c30e681 --- /dev/null +++ b/viscoll-api/spec/factories/projects.rb @@ -0,0 +1,80 @@ +include ActionDispatch::TestProcess +FactoryGirl.define do + sequence :title do |n| + "Project #{n}" + end + sequence :manifest_id do |n| + "Manifest_#{n}" + end + sequence :manifest_name do |n| + "Manifest #{n}" + end + sequence :manifest_url do |n| + "https://iiif.example.org/#{n}/manifest.json" + end + + factory :empty_project, class: Project do + title { generate(:title) } + user_id { FactoryGirl.create(:user) } + end + + factory :project do + transient do + with_members [] + with_manifests [] + end + before(:build) do |project, evaluator| + evaluator.with_manifests.each do |manifest| + mid = evaluator.generate(:manifest_id) + manifest[:id] = mid + project.manifests[mid] = manifest + end + end + after(:create) do |project, evaluator| + evaluator.with_members.each do |member| + member.project_id = project.id + member.nestLevel ||= 1 + member.save + end + unless evaluator.with_members.blank? + project.add_groupIDs(evaluator.with_members.collect { |member| member.id.to_s }, 1) + end + end + title { generate(:title) } + user_id { FactoryGirl.create(:user) } + end + + factory :codex_project, parent: :project do + transient do + manifest_count 0 + quire_structure { [[4, 6]] } + end + before(:build) do |project, evaluator| + evaluator.manifest_count.times do + manifest = FactoryGirl.build(:manifest) + project.manifests[manifest[:id]] = manifest + end + end + after(:create) do |project, evaluator| + start_page = 1 + members = [] + evaluator.quire_structure.each do |qs| + qs[0].times do + members << FactoryGirl.create(:quire, project_id: project.id, leafs: qs[1], start_page: start_page, nestLevel: 1) + start_page += qs[1] + end + end + unless members.blank? + project.add_groupIDs(members.collect { |member| member.id.to_s }, 1) + end + end + end + + factory :manifest, class: Hash do + id { generate(:manifest_id) } + url { generate(:manifest_url) } + name { generate(:manifest_name) } + initialize_with { attributes } + to_create { } + end +end diff --git a/viscoll-api/spec/factories/sides.rb b/viscoll-api/spec/factories/sides.rb new file mode 100644 index 00000000..870fbe9c --- /dev/null +++ b/viscoll-api/spec/factories/sides.rb @@ -0,0 +1,5 @@ +include ActionDispatch::TestProcess +FactoryGirl.define do + factory :side do + end +end diff --git a/viscoll-api/spec/factories/users.rb b/viscoll-api/spec/factories/users.rb new file mode 100644 index 00000000..3de3818e --- /dev/null +++ b/viscoll-api/spec/factories/users.rb @@ -0,0 +1,8 @@ +include ActionDispatch::TestProcess +FactoryGirl.define do + factory :user do + name {Faker::Name.name} + email {Faker::Internet.email} + password {Faker::Internet.password} + end +end diff --git a/viscoll-api/spec/fixtures/base64zip.txt b/viscoll-api/spec/fixtures/base64zip.txt new file mode 100644 index 00000000..513554ed --- /dev/null +++ b/viscoll-api/spec/fixtures/base64zip.txt @@ -0,0 +1 @@ +data:application/zip;base64,UEsDBBQACAAIAMdVrU4AAAAAAAAAAAAAAAAfABAAMVJfNWEyODIyMWVjMTk5ODYwZTdhMmY1ZmQxLnBuZ1VYDADtkdlcNoPZXPcBFADrDPBz5+WS4mJgYOD19HAJAtKMIMzBBiTlRY90giVcHEMqbiX/+T9/oRwDexNTve0Jj/dACQZPVz+XdU4JTQBQSwcIbxz83T8AAABGAAAAUEsDBBQACAAIAMdVrU4AAAAAAAAAAAAAAAAVABAAMlJfMmY1ZmQxc2hpYmFpbnUucG5nVVgMAEGD2Vw2g9lc9wEUAOsM8HPn5ZLiYmBg4PX0cAkC0owgzMEGJOVFj3SCJVwcQypuJf/5P3+hHAN7E1O97QmP90AJBk9XP5d1TglNAFBLBwhvHPzdPwAAAEYAAABQSwMEFAAIAAgAx1WtTgAAAAAAAAAAAAAAAB8AEAAxVl81YTI4MjIxZWMxOTk4NjBlN2EyZjVmZDEucG5nVVgMAEGD2Vw2g9lc9wEUAOsM8HPn5ZLiYmBg4PX0cAkC0owgzMEGJOVFj3SCJVwcQypuJf/5P3+hHAN7E1O97QmP90AJBk9XP5d1TglNAFBLBwhvHPzdPwAAAEYAAABQSwECFQMUAAgACADHVa1Obxz83T8AAABGAAAAHwAMAAAAAAAAAABApIEAAAAAMVJfNWEyODIyMWVjMTk5ODYwZTdhMmY1ZmQxLnBuZ1VYCADtkdlcNoPZXFBLAQIVAxQACAAIAMdVrU5vHPzdPwAAAEYAAAAVAAwAAAAAAAAAAECkgZwAAAAyUl8yZjVmZDFzaGliYWludS5wbmdVWAgAQYPZXDaD2VxQSwECFQMUAAgACADHVa1Obxz83T8AAABGAAAAHwAMAAAAAAAAAABApIEuAQAAMVZfNWEyODIyMWVjMTk5ODYwZTdhMmY1ZmQxLnBuZ1VYCABBg9lcNoPZXFBLBQYAAAAAAwADAAEBAADKAQAAAAA= \ No newline at end of file diff --git a/viscoll-api/spec/fixtures/dots_exported.zip b/viscoll-api/spec/fixtures/dots_exported.zip new file mode 100644 index 00000000..f429e93b Binary files /dev/null and b/viscoll-api/spec/fixtures/dots_exported.zip differ diff --git a/viscoll-api/spec/fixtures/pixel.png b/viscoll-api/spec/fixtures/pixel.png new file mode 100644 index 00000000..0f2de374 Binary files /dev/null and b/viscoll-api/spec/fixtures/pixel.png differ diff --git a/viscoll-api/spec/fixtures/sample_import_json.json b/viscoll-api/spec/fixtures/sample_import_json.json new file mode 100644 index 00000000..8759e829 --- /dev/null +++ b/viscoll-api/spec/fixtures/sample_import_json.json @@ -0,0 +1,295 @@ +{ + "project":{ + "title":"Sample project", + "shelfmark":"Ravenna 384.2339", + "metadata":{ + "date":"18th century" + }, + "preferences":{ + "showTips":true + }, + "manifests":{ + "12341234":{ + "id":"12341234", + "url":"https://digital.library.villanova.edu/Item/vudl:99213/Manifest", + "name":"Boston, and Bunker Hill." + } + }, + "noteTypes":[ + "Hand", + "Ink", + "Unknown" + ] + }, + "Groups":{ + "1":{ + "params":{ + "type":"Quire", + "title":"Quire 1", + "nestLevel":1 + }, + "tacketed":[ + ], + "sewing":[ + ], + "parentOrder":null, + "memberOrders":[ + "Leaf_1", + "Leaf_2", + "Group_2", + "Leaf_5", + "Leaf_6" + ] + }, + "2":{ + "params":{ + "type":"Quire", + "title":"Quire 2", + "nestLevel":2 + }, + "tacketed":[ + ], + "sewing":[ + ], + "parentOrder":1, + "memberOrders":[ + "Leaf_3", + "Leaf_4" + ] + } + }, + "Leafs":{ + "1":{ + "params":{ + "material":"Paper", + "type":"Original", + "attached_above":"None", + "attached_below":"None", + "stub":"None", + "nestLevel":1 + }, + "conjoined_leaf_order":null, + "parentOrder":1, + "rectoOrder":1, + "versoOrder":1 + }, + "2":{ + "params":{ + "material":"Paper", + "type":"Original", + "attached_above":"None", + "attached_below":"None", + "stub":"None", + "nestLevel":1 + }, + "conjoined_leaf_order":null, + "parentOrder":1, + "rectoOrder":2, + "versoOrder":2 + }, + "3":{ + "params":{ + "material":"Paper", + "type":"Original", + "attached_above":"None", + "attached_below":"None", + "stub":"None", + "nestLevel":2 + }, + "conjoined_leaf_order":4, + "parentOrder":2, + "rectoOrder":3, + "versoOrder":3 + }, + "4":{ + "params":{ + "material":"Paper", + "type":"Original", + "attached_above":"None", + "attached_below":"None", + "stub":"None", + "nestLevel":2 + }, + "conjoined_leaf_order":3, + "parentOrder":2, + "rectoOrder":4, + "versoOrder":4 + }, + "5":{ + "params":{ + "material":"Paper", + "type":"Original", + "attached_above":"None", + "attached_below":"None", + "stub":"None", + "nestLevel":1 + }, + "conjoined_leaf_order":null, + "parentOrder":1, + "rectoOrder":5, + "versoOrder":5 + }, + "6":{ + "params":{ + "material":"Paper", + "type":"Endleaf", + "attached_above":"None", + "attached_below":"None", + "stub":"None", + "nestLevel":1 + }, + "conjoined_leaf_order":null, + "parentOrder":1, + "rectoOrder":6, + "versoOrder":6 + } + }, + "Rectos":{ + "1":{ + "params":{ + "folio_number":"1R", + "texture":"Hair", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":1 + }, + "2":{ + "params":{ + "folio_number":"2R", + "texture":"Hair", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":2 + }, + "3":{ + "params":{ + "folio_number":"3R", + "texture":"Hair", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":3 + }, + "4":{ + "params":{ + "folio_number":"4R", + "texture":"Hair", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":4 + }, + "5":{ + "params":{ + "folio_number":"5R", + "texture":"Hair", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":5 + }, + "6":{ + "params":{ + "folio_number":"6R", + "texture":"Hair", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":6 + } + }, + "Versos":{ + "1":{ + "params":{ + "folio_number":"1V", + "texture":"Flesh", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":1 + }, + "2":{ + "params":{ + "folio_number":"2V", + "texture":"Flesh", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":2 + }, + "3":{ + "params":{ + "folio_number":"3V", + "texture":"Flesh", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":3 + }, + "4":{ + "params":{ + "folio_number":"4V", + "texture":"Flesh", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":4 + }, + "5":{ + "params":{ + "folio_number":"5V", + "texture":"Flesh", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":5 + }, + "6":{ + "params":{ + "folio_number":"6V", + "texture":"Flesh", + "image":{ + }, + "script_direction":"None" + }, + "parentOrder":6 + } + }, + "Notes":{ + "1":{ + "params":{ + "title":"Test Note", + "type":"Ink", + "description":"This is a test", + "show":true + }, + "objects":{ + "Group":[ + 1 + ], + "Leaf":[ + 5 + ], + "Recto":[ + 5 + ], + "Verso":[ + 5 + ] + } + } + } +} diff --git a/viscoll-api/spec/fixtures/sample_import_xml.xml b/viscoll-api/spec/fixtures/sample_import_xml.xml new file mode 100644 index 00000000..b976b570 --- /dev/null +++ b/viscoll-api/spec/fixtures/sample_import_xml.xml @@ -0,0 +1,154 @@ + + + + + true + + + + https://digital.library.villanova.edu/Item/vudl:99213/Manifest + + + + Quire 1 + Quire 2 + + + + Quire + + + + #ravenna_384_2339-1-1 #ravenna_384_2339-1-2 #ravenna_384_2339-q-1-2 #ravenna_384_2339-1-3 #ravenna_384_2339-1-4 + #ravenna_384_2339-1-2-3 #ravenna_384_2339-1-2-4 + + + + Paper + + + + Glued (Partial) + Glued (Complete) + Glued (Drumming) + Glued (Other) + Glued (Partial) + Glued (Complete) + Glued (Drumming) + Glued (Other) + + + + Hair + Flesh + + + + Test Note + + + + true + + + Sample project + Ravenna 384.2339 + 18th century + + + 1 + 2 + + + 1 + + + + + + 2 + + + + + + 3 + + + + + + + 4 + + + + + + + 5 + + + + + + 6 + + + + + + + This is a test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/viscoll-api/spec/fixtures/shibainu.jpg b/viscoll-api/spec/fixtures/shibainu.jpg new file mode 100644 index 00000000..c9580521 Binary files /dev/null and b/viscoll-api/spec/fixtures/shibainu.jpg differ diff --git a/viscoll-api/spec/fixtures/uoft_hollar.json b/viscoll-api/spec/fixtures/uoft_hollar.json new file mode 100644 index 00000000..4809cf1d --- /dev/null +++ b/viscoll-api/spec/fixtures/uoft_hollar.json @@ -0,0 +1 @@ +{"@context":"http://iiif.io/api/presentation/2/context.json","@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest","@type":"sc:Manifest","label":"The fables of Aesop / paraphras'd in verse, and adorn'd with sculpture ; by John Ogilby.","attribution":"For rights and reproduction information please contact collections@library.utoronto.ca","sequences":[{"@type":"sc:Sequence","label":"The fables of Aesop / paraphras'd in verse, and adorn'd with sculpture ; by John Ogilby., in order","canvases":[{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0001","@type":"sc:Canvas","label":"Hollar_a_3000_0001","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0001"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0002","@type":"sc:Canvas","label":"Hollar_a_3000_0002","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0002"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0003","@type":"sc:Canvas","label":"Hollar_a_3000_0003","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0003"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0004","@type":"sc:Canvas","label":"Hollar_a_3000_0004","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0004/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0004","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0004/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0004","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0004"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0005","@type":"sc:Canvas","label":"Hollar_a_3000_0005","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0005"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0006","@type":"sc:Canvas","label":"Hollar_a_3000_0006","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0006/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0006","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0006/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0006","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0006"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0007","@type":"sc:Canvas","label":"Hollar_a_3000_0007","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0007"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0008","@type":"sc:Canvas","label":"Hollar_a_3000_0008","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0008/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0008","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0008/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0008","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0008"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0009","@type":"sc:Canvas","label":"Hollar_a_3000_0009","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0009"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0010","@type":"sc:Canvas","label":"Hollar_a_3000_0010","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0010/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0010","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0010/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0010","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0010"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0011","@type":"sc:Canvas","label":"Hollar_a_3000_0011","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0011"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0012","@type":"sc:Canvas","label":"Hollar_a_3000_0012","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0012/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0012","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0012/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0012","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0012"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0013","@type":"sc:Canvas","label":"Hollar_a_3000_0013","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0013/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0013","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0013/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0013","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0013"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0014","@type":"sc:Canvas","label":"Hollar_a_3000_0014","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0014/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0014","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0014/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0014","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0014"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0015","@type":"sc:Canvas","label":"Hollar_a_3000_0015","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0015/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0015","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0015/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0015","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0015"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0016","@type":"sc:Canvas","label":"Hollar_a_3000_0016","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0016/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0016","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4120,"width":2936,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0016/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4120,"width":2936,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0016","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0016"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0017","@type":"sc:Canvas","label":"Hollar_a_3000_0017","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0017/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0017","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2694,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0017/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2694,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0017","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0017"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0018","@type":"sc:Canvas","label":"Hollar_a_3000_0018","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0018/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0018","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0018/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0018","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0018"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0019","@type":"sc:Canvas","label":"Hollar_a_3000_0019","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0019/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0019","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0019/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0019","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0019"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0020","@type":"sc:Canvas","label":"Hollar_a_3000_0020","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0020/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0020","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0020/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0020","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0020"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0021","@type":"sc:Canvas","label":"Hollar_a_3000_0021","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0021/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0021","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0021/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0021","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0021"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0022","@type":"sc:Canvas","label":"Hollar_a_3000_0022","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0022/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0022","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0022/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0022","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0022"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0023","@type":"sc:Canvas","label":"Hollar_a_3000_0023","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0023/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0023","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0023/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0023","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0023"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0024","@type":"sc:Canvas","label":"Hollar_a_3000_0024","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0024/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0024","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0024/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0024","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0024"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0025","@type":"sc:Canvas","label":"Hollar_a_3000_0025","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0025/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0025","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0025/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0025","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0025"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0026","@type":"sc:Canvas","label":"Hollar_a_3000_0026","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0026/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0026","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0026/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0026","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0026"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0027","@type":"sc:Canvas","label":"Hollar_a_3000_0027","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0027/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0027","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0027/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0027","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0027"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0028","@type":"sc:Canvas","label":"Hollar_a_3000_0028","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0028/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0028","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0028/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0028","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0028"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0029","@type":"sc:Canvas","label":"Hollar_a_3000_0029","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0029/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0029","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0029/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0029","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0029"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0030","@type":"sc:Canvas","label":"Hollar_a_3000_0030","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0030/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0030","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0030/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0030","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0030"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0031","@type":"sc:Canvas","label":"Hollar_a_3000_0031","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0031/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0031","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0031/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0031","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0031"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0032","@type":"sc:Canvas","label":"Hollar_a_3000_0032","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0032/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0032","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2850,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0032/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2850,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0032","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0032"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0033","@type":"sc:Canvas","label":"Hollar_a_3000_0033","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0033/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0033","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2730,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0033/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2730,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0033","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0033"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0034","@type":"sc:Canvas","label":"Hollar_a_3000_0034","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0034/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0034","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2826,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0034/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2826,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0034","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0034"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0035","@type":"sc:Canvas","label":"Hollar_a_3000_0035","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0035/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0035","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2826,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0035/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2826,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0035","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0035"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0036","@type":"sc:Canvas","label":"Hollar_a_3000_0036","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0036/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0036","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2826,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0036/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2826,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0036","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0036"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0037","@type":"sc:Canvas","label":"Hollar_a_3000_0037","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0037/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0037","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2694,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0037/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2694,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0037","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0037"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0038","@type":"sc:Canvas","label":"Hollar_a_3000_0038","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0038/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0038","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0038/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0038","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0038"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0039","@type":"sc:Canvas","label":"Hollar_a_3000_0039","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0039/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0039","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0039/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0039","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0039"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0040","@type":"sc:Canvas","label":"Hollar_a_3000_0040","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0040/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0040","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0040/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0040","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0040"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0041","@type":"sc:Canvas","label":"Hollar_a_3000_0041","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0041/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0041","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0041/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0041","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0041"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0042","@type":"sc:Canvas","label":"Hollar_a_3000_0042","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0042/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0042","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0042/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0042","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0042"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0043","@type":"sc:Canvas","label":"Hollar_a_3000_0043","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0043/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0043","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0043/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0043","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0043"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0044","@type":"sc:Canvas","label":"Hollar_a_3000_0044","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0044/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0044","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0044/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0044","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0044"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0045","@type":"sc:Canvas","label":"Hollar_a_3000_0045","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0045/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0045","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0045/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0045","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0045"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0046","@type":"sc:Canvas","label":"Hollar_a_3000_0046","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0046/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0046","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0046/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0046","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0046"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0047","@type":"sc:Canvas","label":"Hollar_a_3000_0047","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0047/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0047","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0047/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0047","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0047"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0048","@type":"sc:Canvas","label":"Hollar_a_3000_0048","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0048/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0048","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0048/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0048","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0048"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0049","@type":"sc:Canvas","label":"Hollar_a_3000_0049","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0049/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0049","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0049/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0049","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0049"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0050","@type":"sc:Canvas","label":"Hollar_a_3000_0050","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0050/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0050","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0050/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0050","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0050"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0051","@type":"sc:Canvas","label":"Hollar_a_3000_0051","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0051/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0051","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0051/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0051","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0051"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0052","@type":"sc:Canvas","label":"Hollar_a_3000_0052","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0052/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0052","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0052/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0052","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0052"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0053","@type":"sc:Canvas","label":"Hollar_a_3000_0053","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0053/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0053","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0053/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0053","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0053"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0054","@type":"sc:Canvas","label":"Hollar_a_3000_0054","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0054/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0054","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0054/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0054","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0054"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0055","@type":"sc:Canvas","label":"Hollar_a_3000_0055","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0055/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0055","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0055/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0055","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0055"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0056","@type":"sc:Canvas","label":"Hollar_a_3000_0056","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0056/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0056","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0056/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0056","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0056"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0057","@type":"sc:Canvas","label":"Hollar_a_3000_0057","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0057/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0057","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0057/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0057","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0057"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0058","@type":"sc:Canvas","label":"Hollar_a_3000_0058","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0058/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0058","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0058/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0058","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0058"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0059","@type":"sc:Canvas","label":"Hollar_a_3000_0059","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0059/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0059","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0059/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0059","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0059"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0060","@type":"sc:Canvas","label":"Hollar_a_3000_0060","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0060/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0060","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0060/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0060","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0060"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0061","@type":"sc:Canvas","label":"Hollar_a_3000_0061","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0061/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0061","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0061/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0061","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0061"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0062","@type":"sc:Canvas","label":"Hollar_a_3000_0062","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0062/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0062","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0062/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0062","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0062"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0063","@type":"sc:Canvas","label":"Hollar_a_3000_0063","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0063/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0063","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0063/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0063","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0063"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0064","@type":"sc:Canvas","label":"Hollar_a_3000_0064","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0064/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0064","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0064/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0064","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0064"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0065","@type":"sc:Canvas","label":"Hollar_a_3000_0065","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0065/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0065","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0065/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0065","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0065"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0066","@type":"sc:Canvas","label":"Hollar_a_3000_0066","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0066/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0066","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0066/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0066","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0066"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0067","@type":"sc:Canvas","label":"Hollar_a_3000_0067","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0067/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0067","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0067/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0067","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0067"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0068","@type":"sc:Canvas","label":"Hollar_a_3000_0068","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0068/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0068","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0068/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0068","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0068"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0069","@type":"sc:Canvas","label":"Hollar_a_3000_0069","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0069/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0069","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0069/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0069","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0069"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0070","@type":"sc:Canvas","label":"Hollar_a_3000_0070","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0070/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0070","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0070/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0070","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0070"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0071","@type":"sc:Canvas","label":"Hollar_a_3000_0071","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0071/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0071","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0071/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0071","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0071"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0072","@type":"sc:Canvas","label":"Hollar_a_3000_0072","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0072/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0072","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0072/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0072","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0072"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0073","@type":"sc:Canvas","label":"Hollar_a_3000_0073","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0073/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0073","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0073/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0073","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0073"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0074","@type":"sc:Canvas","label":"Hollar_a_3000_0074","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0074/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0074","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0074/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0074","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0074"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0075","@type":"sc:Canvas","label":"Hollar_a_3000_0075","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0075/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0075","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0075/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0075","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0075"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0076","@type":"sc:Canvas","label":"Hollar_a_3000_0076","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0076/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0076","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0076/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0076","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0076"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0077","@type":"sc:Canvas","label":"Hollar_a_3000_0077","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0077/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0077","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0077/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0077","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0077"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0078","@type":"sc:Canvas","label":"Hollar_a_3000_0078","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0078/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0078","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0078/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0078","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0078"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0079","@type":"sc:Canvas","label":"Hollar_a_3000_0079","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0079/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0079","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0079/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0079","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0079"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0080","@type":"sc:Canvas","label":"Hollar_a_3000_0080","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0080/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0080","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0080/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0080","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0080"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0081","@type":"sc:Canvas","label":"Hollar_a_3000_0081","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0081/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0081","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0081/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0081","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0081"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0082","@type":"sc:Canvas","label":"Hollar_a_3000_0082","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0082/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0082","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0082/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0082","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0082"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0083","@type":"sc:Canvas","label":"Hollar_a_3000_0083","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0083/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0083","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0083/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0083","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0083"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0084","@type":"sc:Canvas","label":"Hollar_a_3000_0084","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0084/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0084","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0084/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0084","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0084"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0085","@type":"sc:Canvas","label":"Hollar_a_3000_0085","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0085/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0085","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0085/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0085","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0085"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0086","@type":"sc:Canvas","label":"Hollar_a_3000_0086","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0086/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0086","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0086/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0086","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0086"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0087","@type":"sc:Canvas","label":"Hollar_a_3000_0087","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0087/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0087","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0087/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0087","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0087"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0088","@type":"sc:Canvas","label":"Hollar_a_3000_0088","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0088/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0088","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0088/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0088","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0088"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0089","@type":"sc:Canvas","label":"Hollar_a_3000_0089","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0089/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0089","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0089/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0089","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0089"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0090","@type":"sc:Canvas","label":"Hollar_a_3000_0090","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0090/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0090","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0090/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0090","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0090"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0091","@type":"sc:Canvas","label":"Hollar_a_3000_0091","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0091/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0091","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0091/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0091","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0091"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0092","@type":"sc:Canvas","label":"Hollar_a_3000_0092","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0092/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0092","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0092/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0092","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0092"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0093","@type":"sc:Canvas","label":"Hollar_a_3000_0093","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0093/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0093","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0093/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0093","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0093"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0094","@type":"sc:Canvas","label":"Hollar_a_3000_0094","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0094/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0094","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0094/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0094","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0094"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0095","@type":"sc:Canvas","label":"Hollar_a_3000_0095","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0095/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0095","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0095/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0095","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0095"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0096","@type":"sc:Canvas","label":"Hollar_a_3000_0096","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0096/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0096","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0096/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0096","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0096"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0097","@type":"sc:Canvas","label":"Hollar_a_3000_0097","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0097/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0097","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0097/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0097","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0097"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0098","@type":"sc:Canvas","label":"Hollar_a_3000_0098","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0098/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0098","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0098/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0098","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0098"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0099","@type":"sc:Canvas","label":"Hollar_a_3000_0099","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0099/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0099","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0099/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0099","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0099"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0100","@type":"sc:Canvas","label":"Hollar_a_3000_0100","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0100/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0100","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0100/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0100","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0100"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0101","@type":"sc:Canvas","label":"Hollar_a_3000_0101","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0101/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0101","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0101/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0101","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0101"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0102","@type":"sc:Canvas","label":"Hollar_a_3000_0102","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0102/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0102","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0102/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0102","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0102"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0103","@type":"sc:Canvas","label":"Hollar_a_3000_0103","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0103/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0103","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0103/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0103","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0103"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0104","@type":"sc:Canvas","label":"Hollar_a_3000_0104","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0104/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0104","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0104/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0104","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0104"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0105","@type":"sc:Canvas","label":"Hollar_a_3000_0105","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0105/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0105","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0105/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0105","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0105"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0106","@type":"sc:Canvas","label":"Hollar_a_3000_0106","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0106/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0106","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0106/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0106","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0106"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0107","@type":"sc:Canvas","label":"Hollar_a_3000_0107","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0107/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0107","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0107/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0107","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0107"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0108","@type":"sc:Canvas","label":"Hollar_a_3000_0108","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0108/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0108","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0108/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0108","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0108"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0109","@type":"sc:Canvas","label":"Hollar_a_3000_0109","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0109/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0109","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0109/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0109","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0109"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0110","@type":"sc:Canvas","label":"Hollar_a_3000_0110","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0110/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0110","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0110/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0110","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0110"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0111","@type":"sc:Canvas","label":"Hollar_a_3000_0111","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0111/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0111","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0111/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0111","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0111"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0112","@type":"sc:Canvas","label":"Hollar_a_3000_0112","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0112/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0112","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0112/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0112","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0112"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0113","@type":"sc:Canvas","label":"Hollar_a_3000_0113","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0113/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0113","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0113/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0113","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0113"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0114","@type":"sc:Canvas","label":"Hollar_a_3000_0114","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0114/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0114","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0114/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0114","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0114"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0115","@type":"sc:Canvas","label":"Hollar_a_3000_0115","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0115/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0115","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0115/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0115","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0115"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0116","@type":"sc:Canvas","label":"Hollar_a_3000_0116","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0116/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0116","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0116/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0116","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0116"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0117","@type":"sc:Canvas","label":"Hollar_a_3000_0117","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0117/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0117","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0117/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0117","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0117"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0118","@type":"sc:Canvas","label":"Hollar_a_3000_0118","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0118/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0118","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0118/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0118","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0118"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0119","@type":"sc:Canvas","label":"Hollar_a_3000_0119","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0119/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0119","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0119/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0119","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0119"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0120","@type":"sc:Canvas","label":"Hollar_a_3000_0120","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0120/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0120","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0120/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0120","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0120"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0121","@type":"sc:Canvas","label":"Hollar_a_3000_0121","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0121/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0121","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0121/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0121","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0121"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0122","@type":"sc:Canvas","label":"Hollar_a_3000_0122","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0122/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0122","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0122/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0122","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0122"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0123","@type":"sc:Canvas","label":"Hollar_a_3000_0123","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0123/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0123","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0123/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0123","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0123"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0124","@type":"sc:Canvas","label":"Hollar_a_3000_0124","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0124/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0124","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0124/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0124","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0124"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0125","@type":"sc:Canvas","label":"Hollar_a_3000_0125","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0125/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0125","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0125/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0125","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0125"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0126","@type":"sc:Canvas","label":"Hollar_a_3000_0126","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0126/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0126","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0126/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0126","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0126"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0127","@type":"sc:Canvas","label":"Hollar_a_3000_0127","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0127/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0127","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0127/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0127","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0127"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0128","@type":"sc:Canvas","label":"Hollar_a_3000_0128","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0128/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0128","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0128/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0128","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0128"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0129","@type":"sc:Canvas","label":"Hollar_a_3000_0129","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0129/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0129","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0129/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0129","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0129"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0130","@type":"sc:Canvas","label":"Hollar_a_3000_0130","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0130/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0130","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0130/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0130","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0130"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0131","@type":"sc:Canvas","label":"Hollar_a_3000_0131","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0131/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0131","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0131/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0131","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0131"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0132","@type":"sc:Canvas","label":"Hollar_a_3000_0132","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0132/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0132","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0132/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0132","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0132"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0133","@type":"sc:Canvas","label":"Hollar_a_3000_0133","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0133/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0133","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0133/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0133","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0133"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0134","@type":"sc:Canvas","label":"Hollar_a_3000_0134","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0134/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0134","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0134/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0134","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0134"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0135","@type":"sc:Canvas","label":"Hollar_a_3000_0135","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0135/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0135","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0135/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0135","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0135"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0136","@type":"sc:Canvas","label":"Hollar_a_3000_0136","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0136/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0136","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0136/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0136","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0136"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0137","@type":"sc:Canvas","label":"Hollar_a_3000_0137","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0137/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0137","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0137/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0137","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0137"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0138","@type":"sc:Canvas","label":"Hollar_a_3000_0138","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0138/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0138","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0138/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0138","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0138"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0139","@type":"sc:Canvas","label":"Hollar_a_3000_0139","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0139/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0139","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0139/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0139","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0139"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0140","@type":"sc:Canvas","label":"Hollar_a_3000_0140","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0140/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0140","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0140/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0140","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0140"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0141","@type":"sc:Canvas","label":"Hollar_a_3000_0141","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0141/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0141","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0141/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0141","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0141"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0142","@type":"sc:Canvas","label":"Hollar_a_3000_0142","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0142/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0142","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0142/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0142","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0142"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0143","@type":"sc:Canvas","label":"Hollar_a_3000_0143","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0143/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0143","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0143/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0143","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0143"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0144","@type":"sc:Canvas","label":"Hollar_a_3000_0144","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0144/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0144","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0144/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0144","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0144"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0145","@type":"sc:Canvas","label":"Hollar_a_3000_0145","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0145/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0145","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0145/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0145","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0145"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0146","@type":"sc:Canvas","label":"Hollar_a_3000_0146","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0146/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0146","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0146/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0146","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0146"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0147","@type":"sc:Canvas","label":"Hollar_a_3000_0147","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0147/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0147","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0147/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0147","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0147"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0148","@type":"sc:Canvas","label":"Hollar_a_3000_0148","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0148/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0148","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0148/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0148","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0148"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0149","@type":"sc:Canvas","label":"Hollar_a_3000_0149","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0149/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0149","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0149/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0149","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0149"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0150","@type":"sc:Canvas","label":"Hollar_a_3000_0150","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0150/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0150","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0150/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0150","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0150"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0151","@type":"sc:Canvas","label":"Hollar_a_3000_0151","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0151/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0151","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0151/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0151","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0151"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0152","@type":"sc:Canvas","label":"Hollar_a_3000_0152","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0152/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0152","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0152/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0152","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0152"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0153","@type":"sc:Canvas","label":"Hollar_a_3000_0153","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0153/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0153","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0153/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0153","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0153"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0154","@type":"sc:Canvas","label":"Hollar_a_3000_0154","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0154/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0154","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0154/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0154","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0154"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0155","@type":"sc:Canvas","label":"Hollar_a_3000_0155","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0155/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0155","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0155/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0155","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0155"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0156","@type":"sc:Canvas","label":"Hollar_a_3000_0156","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0156/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0156","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0156/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0156","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0156"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0157","@type":"sc:Canvas","label":"Hollar_a_3000_0157","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0157/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0157","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0157/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0157","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0157"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0158","@type":"sc:Canvas","label":"Hollar_a_3000_0158","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0158/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0158","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0158/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0158","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0158"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0159","@type":"sc:Canvas","label":"Hollar_a_3000_0159","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0159/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0159","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0159/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0159","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0159"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0160","@type":"sc:Canvas","label":"Hollar_a_3000_0160","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0160/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0160","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0160/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0160","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0160"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0161","@type":"sc:Canvas","label":"Hollar_a_3000_0161","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0161/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0161","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0161/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0161","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0161"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0162","@type":"sc:Canvas","label":"Hollar_a_3000_0162","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0162/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0162","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0162/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0162","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0162"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0163","@type":"sc:Canvas","label":"Hollar_a_3000_0163","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0163/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0163","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0163/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0163","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0163"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0164","@type":"sc:Canvas","label":"Hollar_a_3000_0164","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0164/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0164","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0164/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0164","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0164"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0165","@type":"sc:Canvas","label":"Hollar_a_3000_0165","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0165/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0165","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0165/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0165","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0165"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0166","@type":"sc:Canvas","label":"Hollar_a_3000_0166","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0166/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0166","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0166/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0166","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0166"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0167","@type":"sc:Canvas","label":"Hollar_a_3000_0167","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0167/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0167","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0167/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0167","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0167"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0168","@type":"sc:Canvas","label":"Hollar_a_3000_0168","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0168/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0168","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0168/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0168","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0168"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0169","@type":"sc:Canvas","label":"Hollar_a_3000_0169","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0169/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0169","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0169/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0169","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0169"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0170","@type":"sc:Canvas","label":"Hollar_a_3000_0170","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0170/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0170","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0170/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0170","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0170"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0171","@type":"sc:Canvas","label":"Hollar_a_3000_0171","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0171/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0171","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0171/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0171","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0171"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0172","@type":"sc:Canvas","label":"Hollar_a_3000_0172","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0172/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0172","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0172/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0172","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0172"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0173","@type":"sc:Canvas","label":"Hollar_a_3000_0173","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0173/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0173","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0173/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0173","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0173"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0174","@type":"sc:Canvas","label":"Hollar_a_3000_0174","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0174/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0174","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0174/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0174","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0174"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0175","@type":"sc:Canvas","label":"Hollar_a_3000_0175","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0175/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0175","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0175/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0175","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0175"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0176","@type":"sc:Canvas","label":"Hollar_a_3000_0176","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0176/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0176","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0176/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0176","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0176"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0177","@type":"sc:Canvas","label":"Hollar_a_3000_0177","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0177/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0177","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0177/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0177","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0177"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0178","@type":"sc:Canvas","label":"Hollar_a_3000_0178","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0178/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0178","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0178/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0178","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0178"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0179","@type":"sc:Canvas","label":"Hollar_a_3000_0179","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0179/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0179","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0179/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0179","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0179"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0180","@type":"sc:Canvas","label":"Hollar_a_3000_0180","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0180/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0180","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0180/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0180","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0180"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0181","@type":"sc:Canvas","label":"Hollar_a_3000_0181","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0181/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0181","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0181/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0181","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0181"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0182","@type":"sc:Canvas","label":"Hollar_a_3000_0182","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0182/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0182","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0182/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0182","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0182"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0183","@type":"sc:Canvas","label":"Hollar_a_3000_0183","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0183/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0183","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0183/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0183","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0183"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0184","@type":"sc:Canvas","label":"Hollar_a_3000_0184","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0184/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0184","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0184/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0184","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0184"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0185","@type":"sc:Canvas","label":"Hollar_a_3000_0185","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0185/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0185","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0185/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0185","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0185"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0186","@type":"sc:Canvas","label":"Hollar_a_3000_0186","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0186/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0186","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0186/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0186","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0186"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0187","@type":"sc:Canvas","label":"Hollar_a_3000_0187","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0187/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0187","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0187/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0187","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0187"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0188","@type":"sc:Canvas","label":"Hollar_a_3000_0188","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0188/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0188","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0188/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0188","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0188"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0189","@type":"sc:Canvas","label":"Hollar_a_3000_0189","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0189/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0189","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0189/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0189","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0189"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0190","@type":"sc:Canvas","label":"Hollar_a_3000_0190","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0190/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0190","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0190/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0190","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0190"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0191","@type":"sc:Canvas","label":"Hollar_a_3000_0191","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0191/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0191","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0191/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0191","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0191"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0192","@type":"sc:Canvas","label":"Hollar_a_3000_0192","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0192/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0192","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0192/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0192","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0192"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0193","@type":"sc:Canvas","label":"Hollar_a_3000_0193","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0193/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0193","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0193/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0193","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0193"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0194","@type":"sc:Canvas","label":"Hollar_a_3000_0194","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0194/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0194","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0194/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0194","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0194"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0195","@type":"sc:Canvas","label":"Hollar_a_3000_0195","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0195/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0195","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0195/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0195","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0195"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0196","@type":"sc:Canvas","label":"Hollar_a_3000_0196","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0196/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0196","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0196/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0196","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0196"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0197","@type":"sc:Canvas","label":"Hollar_a_3000_0197","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0197/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0197","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0197/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0197","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0197"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0198","@type":"sc:Canvas","label":"Hollar_a_3000_0198","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0198/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0198","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0198/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0198","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0198"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0199","@type":"sc:Canvas","label":"Hollar_a_3000_0199","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0199/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0199","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0199/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0199","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0199"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0200","@type":"sc:Canvas","label":"Hollar_a_3000_0200","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0200/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0200","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0200/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0200","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0200"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0201","@type":"sc:Canvas","label":"Hollar_a_3000_0201","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0201/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0201","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0201/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0201","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0201"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0202","@type":"sc:Canvas","label":"Hollar_a_3000_0202","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0202/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0202","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0202/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0202","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0202"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0203","@type":"sc:Canvas","label":"Hollar_a_3000_0203","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0203/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0203","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0203/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0203","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0203"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0204","@type":"sc:Canvas","label":"Hollar_a_3000_0204","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0204/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0204","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0204/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0204","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0204"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0205","@type":"sc:Canvas","label":"Hollar_a_3000_0205","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0205/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0205","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0205/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0205","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0205"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0206","@type":"sc:Canvas","label":"Hollar_a_3000_0206","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0206/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0206","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0206/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0206","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0206"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0207","@type":"sc:Canvas","label":"Hollar_a_3000_0207","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0207/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0207","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0207/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0207","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0207"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0208","@type":"sc:Canvas","label":"Hollar_a_3000_0208","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0208/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0208","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0208/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0208","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0208"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0209","@type":"sc:Canvas","label":"Hollar_a_3000_0209","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0209/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0209","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0209/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0209","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0209"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0210","@type":"sc:Canvas","label":"Hollar_a_3000_0210","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0210/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0210","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0210/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0210","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0210"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0211","@type":"sc:Canvas","label":"Hollar_a_3000_0211","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0211/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0211","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0211/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0211","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0211"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0212","@type":"sc:Canvas","label":"Hollar_a_3000_0212","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0212/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0212","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0212/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0212","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0212"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0213","@type":"sc:Canvas","label":"Hollar_a_3000_0213","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0213/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0213","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0213/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0213","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0213"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0214","@type":"sc:Canvas","label":"Hollar_a_3000_0214","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0214/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0214","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0214/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0214","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0214"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0215","@type":"sc:Canvas","label":"Hollar_a_3000_0215","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0215/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0215","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0215/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0215","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0215"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0216","@type":"sc:Canvas","label":"Hollar_a_3000_0216","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0216/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0216","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0216/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0216","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0216"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0217","@type":"sc:Canvas","label":"Hollar_a_3000_0217","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0217/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0217","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0217/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0217","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0217"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0218","@type":"sc:Canvas","label":"Hollar_a_3000_0218","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0218/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0218","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0218/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0218","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0218"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0219","@type":"sc:Canvas","label":"Hollar_a_3000_0219","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0219/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0219","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0219/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0219","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0219"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0220","@type":"sc:Canvas","label":"Hollar_a_3000_0220","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0220/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0220","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0220/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0220","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0220"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0221","@type":"sc:Canvas","label":"Hollar_a_3000_0221","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0221/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0221","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0221/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0221","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0221"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0222","@type":"sc:Canvas","label":"Hollar_a_3000_0222","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0222/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0222","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0222/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0222","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0222"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0223","@type":"sc:Canvas","label":"Hollar_a_3000_0223","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0223/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0223","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0223/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0223","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0223"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0224","@type":"sc:Canvas","label":"Hollar_a_3000_0224","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0224/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0224","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0224/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0224","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0224"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0225","@type":"sc:Canvas","label":"Hollar_a_3000_0225","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0225/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0225","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0225/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0225","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0225"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0226","@type":"sc:Canvas","label":"Hollar_a_3000_0226","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0226/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0226","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0226/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0226","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0226"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0227","@type":"sc:Canvas","label":"Hollar_a_3000_0227","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0227/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0227","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0227/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0227","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0227"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0228","@type":"sc:Canvas","label":"Hollar_a_3000_0228","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0228/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0228","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0228/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0228","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0228"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0229","@type":"sc:Canvas","label":"Hollar_a_3000_0229","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0229/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0229","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0229/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0229","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0229"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0230","@type":"sc:Canvas","label":"Hollar_a_3000_0230","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0230/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0230","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0230/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0230","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0230"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0231","@type":"sc:Canvas","label":"Hollar_a_3000_0231","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0231/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0231","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0231/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0231","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0231"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0232","@type":"sc:Canvas","label":"Hollar_a_3000_0232","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0232/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0232","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0232/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0232","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0232"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0233","@type":"sc:Canvas","label":"Hollar_a_3000_0233","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0233/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0233","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4106,"width":2790,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0233/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4106,"width":2790,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0233","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0233"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0234","@type":"sc:Canvas","label":"Hollar_a_3000_0234","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0234/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0234","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0234/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0234","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0234"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0235","@type":"sc:Canvas","label":"Hollar_a_3000_0235","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0235/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0235","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0235/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0235","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0235"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0236","@type":"sc:Canvas","label":"Hollar_a_3000_0236","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0236/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0236","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0236/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0236","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0236"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0237","@type":"sc:Canvas","label":"Hollar_a_3000_0237","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0237/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0237","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0237/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0237","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0237"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0238","@type":"sc:Canvas","label":"Hollar_a_3000_0238","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0238/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0238","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0238/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0238","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0238"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0239","@type":"sc:Canvas","label":"Hollar_a_3000_0239","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0239/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0239","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0239/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0239","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0239"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0240","@type":"sc:Canvas","label":"Hollar_a_3000_0240","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0240/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0240","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0240/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0240","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0240"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0241","@type":"sc:Canvas","label":"Hollar_a_3000_0241","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0241/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0241","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0241/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0241","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0241"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0242","@type":"sc:Canvas","label":"Hollar_a_3000_0242","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0242/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0242","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0242/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0242","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0242"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0243","@type":"sc:Canvas","label":"Hollar_a_3000_0243","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0243/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0243","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0243/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0243","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0243"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0244","@type":"sc:Canvas","label":"Hollar_a_3000_0244","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0244/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0244","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0244/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0244","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0244"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0245","@type":"sc:Canvas","label":"Hollar_a_3000_0245","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0245/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0245","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0245/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0245","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0245"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0246","@type":"sc:Canvas","label":"Hollar_a_3000_0246","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0246/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0246","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0246/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0246","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0246"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0247","@type":"sc:Canvas","label":"Hollar_a_3000_0247","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0247/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0247","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0247/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0247","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0247"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0248","@type":"sc:Canvas","label":"Hollar_a_3000_0248","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0248/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0248","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0248/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0248","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0248"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0249","@type":"sc:Canvas","label":"Hollar_a_3000_0249","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0249/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0249","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0249/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0249","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0249"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0250","@type":"sc:Canvas","label":"Hollar_a_3000_0250","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0250/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0250","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0250/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0250","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0250"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0251","@type":"sc:Canvas","label":"Hollar_a_3000_0251","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0251/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0251","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0251/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0251","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0251"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0252","@type":"sc:Canvas","label":"Hollar_a_3000_0252","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0252/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0252","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0252/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0252","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0252"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0253","@type":"sc:Canvas","label":"Hollar_a_3000_0253","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0253/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0253","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0253/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0253","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0253"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0254","@type":"sc:Canvas","label":"Hollar_a_3000_0254","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0254/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0254","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0254/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0254","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0254"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0255","@type":"sc:Canvas","label":"Hollar_a_3000_0255","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0255/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0255","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0255/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0255","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0255"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0256","@type":"sc:Canvas","label":"Hollar_a_3000_0256","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0256/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0256","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0256/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0256","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0256"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0257","@type":"sc:Canvas","label":"Hollar_a_3000_0257","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0257/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0257","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0257/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0257","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0257"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0258","@type":"sc:Canvas","label":"Hollar_a_3000_0258","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0258/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0258","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0258/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0258","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0258"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0259","@type":"sc:Canvas","label":"Hollar_a_3000_0259","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0259/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0259","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0259/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0259","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0259"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0260","@type":"sc:Canvas","label":"Hollar_a_3000_0260","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0260/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0260","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0260/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0260","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0260"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0261","@type":"sc:Canvas","label":"Hollar_a_3000_0261","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0261/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0261","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0261/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0261","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0261"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0262","@type":"sc:Canvas","label":"Hollar_a_3000_0262","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0262/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0262","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0262/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0262","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0262"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0263","@type":"sc:Canvas","label":"Hollar_a_3000_0263","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0263/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0263","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0263/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0263","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0263"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0264","@type":"sc:Canvas","label":"Hollar_a_3000_0264","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0264/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0264","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0264/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0264","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0264"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0265","@type":"sc:Canvas","label":"Hollar_a_3000_0265","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0265/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0265","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2788,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0265/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2788,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0265","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0265"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0266","@type":"sc:Canvas","label":"Hollar_a_3000_0266","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0266/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0266","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2944,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0266/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2944,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0266","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0266"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0267","@type":"sc:Canvas","label":"Hollar_a_3000_0267","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0267/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0267","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2944,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0267/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2944,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0267","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0267"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0268","@type":"sc:Canvas","label":"Hollar_a_3000_0268","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0268/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0268","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2848,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0268/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2848,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0268","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0268"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0269","@type":"sc:Canvas","label":"Hollar_a_3000_0269","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0269/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0269","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2848,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0269/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2848,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0269","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0269"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0270","@type":"sc:Canvas","label":"Hollar_a_3000_0270","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0270/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0270","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2848,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0270/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2848,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0270","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0270"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0271","@type":"sc:Canvas","label":"Hollar_a_3000_0271","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0271/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0271","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2728,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0271/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2728,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0271","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0271"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0272","@type":"sc:Canvas","label":"Hollar_a_3000_0272","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0272/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0272","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0272/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0272","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0272"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0273","@type":"sc:Canvas","label":"Hollar_a_3000_0273","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0273/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0273","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0273/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0273","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0273"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0274","@type":"sc:Canvas","label":"Hollar_a_3000_0274","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0274/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0274","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0274/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0274","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0274"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0275","@type":"sc:Canvas","label":"Hollar_a_3000_0275","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0275/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0275","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0275/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0275","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0275"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0276","@type":"sc:Canvas","label":"Hollar_a_3000_0276","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0276/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0276","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0276/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0276","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0276"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0277","@type":"sc:Canvas","label":"Hollar_a_3000_0277","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0277/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0277","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0277/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0277","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0277"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0278","@type":"sc:Canvas","label":"Hollar_a_3000_0278","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0278/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0278","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0278/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0278","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0278"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0279","@type":"sc:Canvas","label":"Hollar_a_3000_0279","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0279/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0279","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0279/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0279","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0279"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0280","@type":"sc:Canvas","label":"Hollar_a_3000_0280","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0280/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0280","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0280/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0280","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0280"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0281","@type":"sc:Canvas","label":"Hollar_a_3000_0281","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0281/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0281","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0281/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0281","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0281"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0282","@type":"sc:Canvas","label":"Hollar_a_3000_0282","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0282/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0282","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0282/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0282","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0282"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0283","@type":"sc:Canvas","label":"Hollar_a_3000_0283","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0283/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0283","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0283/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0283","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0283"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0284","@type":"sc:Canvas","label":"Hollar_a_3000_0284","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0284/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0284","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0284/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0284","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0284"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0285","@type":"sc:Canvas","label":"Hollar_a_3000_0285","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0285/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0285","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0285/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0285","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0285"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0286","@type":"sc:Canvas","label":"Hollar_a_3000_0286","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0286/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0286","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0286/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0286","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0286"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0287","@type":"sc:Canvas","label":"Hollar_a_3000_0287","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0287/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0287","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0287/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0287","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0287"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0288","@type":"sc:Canvas","label":"Hollar_a_3000_0288","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0288/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0288","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0288/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0288","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0288"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0289","@type":"sc:Canvas","label":"Hollar_a_3000_0289","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0289/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0289","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0289/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0289","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0289"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0290","@type":"sc:Canvas","label":"Hollar_a_3000_0290","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0290/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0290","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0290/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0290","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0290"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0291","@type":"sc:Canvas","label":"Hollar_a_3000_0291","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0291/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0291","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0291/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0291","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0291"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0292","@type":"sc:Canvas","label":"Hollar_a_3000_0292","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0292/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0292","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0292/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0292","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0292"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0293","@type":"sc:Canvas","label":"Hollar_a_3000_0293","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0293/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0293","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0293/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0293","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0293"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0294","@type":"sc:Canvas","label":"Hollar_a_3000_0294","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0294/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0294","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0294/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0294","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0294"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0295","@type":"sc:Canvas","label":"Hollar_a_3000_0295","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0295/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0295","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4020,"width":2896,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0295/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4020,"width":2896,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0295","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0295"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0296","@type":"sc:Canvas","label":"Hollar_a_3000_0296","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0296/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0296","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0296/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0296","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0296"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0297","@type":"sc:Canvas","label":"Hollar_a_3000_0297","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0297/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0297","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0297/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0297","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0297"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0298","@type":"sc:Canvas","label":"Hollar_a_3000_0298","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0298/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0298","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0298/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0298","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0298"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0299","@type":"sc:Canvas","label":"Hollar_a_3000_0299","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0299/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0299","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0299/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0299","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0299"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0300","@type":"sc:Canvas","label":"Hollar_a_3000_0300","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0300/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0300","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0300/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0300","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0300"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0301","@type":"sc:Canvas","label":"Hollar_a_3000_0301","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0301/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0301","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0301/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0301","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0301"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0302","@type":"sc:Canvas","label":"Hollar_a_3000_0302","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0302/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0302","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0302/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0302","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0302"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0303","@type":"sc:Canvas","label":"Hollar_a_3000_0303","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0303/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0303","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0303/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0303","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0303"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0304","@type":"sc:Canvas","label":"Hollar_a_3000_0304","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0304/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0304","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0304/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0304","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0304"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0305","@type":"sc:Canvas","label":"Hollar_a_3000_0305","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0305/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0305","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0305/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0305","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0305"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0306","@type":"sc:Canvas","label":"Hollar_a_3000_0306","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0306/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0306","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0306/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0306","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0306"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0307","@type":"sc:Canvas","label":"Hollar_a_3000_0307","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0307/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0307","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0307/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0307","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0307"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0308","@type":"sc:Canvas","label":"Hollar_a_3000_0308","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0308/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0308","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0308/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0308","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0308"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0309","@type":"sc:Canvas","label":"Hollar_a_3000_0309","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0309/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0309","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0309/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0309","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0309"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0310","@type":"sc:Canvas","label":"Hollar_a_3000_0310","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0310/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0310","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0310/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0310","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0310"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0311","@type":"sc:Canvas","label":"Hollar_a_3000_0311","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0311/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0311","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0311/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0311","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0311"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0312","@type":"sc:Canvas","label":"Hollar_a_3000_0312","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0312/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0312","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0312/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0312","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0312"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0313","@type":"sc:Canvas","label":"Hollar_a_3000_0313","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0313/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0313","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0313/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0313","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0313"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0314","@type":"sc:Canvas","label":"Hollar_a_3000_0314","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0314/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0314","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0314/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0314","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0314"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0315","@type":"sc:Canvas","label":"Hollar_a_3000_0315","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0315/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0315","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0315/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0315","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0315"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0316","@type":"sc:Canvas","label":"Hollar_a_3000_0316","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0316/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0316","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0316/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0316","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0316"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0317","@type":"sc:Canvas","label":"Hollar_a_3000_0317","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0317/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0317","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0317/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0317","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0317"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0318","@type":"sc:Canvas","label":"Hollar_a_3000_0318","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0318/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0318","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0318/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0318","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0318"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0319","@type":"sc:Canvas","label":"Hollar_a_3000_0319","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0319/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0319","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0319/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0319","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0319"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0320","@type":"sc:Canvas","label":"Hollar_a_3000_0320","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0320/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0320","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0320/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0320","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0320"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0321","@type":"sc:Canvas","label":"Hollar_a_3000_0321","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0321/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0321","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0321/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0321","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0321"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0322","@type":"sc:Canvas","label":"Hollar_a_3000_0322","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0322/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0322","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0322/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0322","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0322"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0323","@type":"sc:Canvas","label":"Hollar_a_3000_0323","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0323/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0323","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0323/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0323","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0323"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0324","@type":"sc:Canvas","label":"Hollar_a_3000_0324","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0324/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0324","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0324/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0324","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0324"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0325","@type":"sc:Canvas","label":"Hollar_a_3000_0325","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0325/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0325","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0325/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0325","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0325"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0326","@type":"sc:Canvas","label":"Hollar_a_3000_0326","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0326/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0326","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0326/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0326","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0326"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0327","@type":"sc:Canvas","label":"Hollar_a_3000_0327","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0327/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0327","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0327/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0327","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0327"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0328","@type":"sc:Canvas","label":"Hollar_a_3000_0328","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0328/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0328","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0328/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0328","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0328"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0329","@type":"sc:Canvas","label":"Hollar_a_3000_0329","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0329/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0329","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0329/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0329","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0329"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0330","@type":"sc:Canvas","label":"Hollar_a_3000_0330","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0330/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0330","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0330/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0330","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0330"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0331","@type":"sc:Canvas","label":"Hollar_a_3000_0331","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0331/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0331","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0331/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0331","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0331"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0332","@type":"sc:Canvas","label":"Hollar_a_3000_0332","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0332/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0332","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0332/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0332","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0332"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0333","@type":"sc:Canvas","label":"Hollar_a_3000_0333","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0333/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0333","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4104,"width":2786,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0333/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4104,"width":2786,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0333","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0333"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0334","@type":"sc:Canvas","label":"Hollar_a_3000_0334","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0334/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0334","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3981,"width":2891,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0334/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3981,"width":2891,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0334","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0334"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0335","@type":"sc:Canvas","label":"Hollar_a_3000_0335","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0335/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0335","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2682,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0335/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2682,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0335","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0335"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0336","@type":"sc:Canvas","label":"Hollar_a_3000_0336","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0336/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0336","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2915,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0336/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2915,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0336","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0336"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0337","@type":"sc:Canvas","label":"Hollar_a_3000_0337","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0337/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0337","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2622,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0337/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2622,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0337","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0337"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0338","@type":"sc:Canvas","label":"Hollar_a_3000_0338","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0338/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0338","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2903,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0338/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2903,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0338","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0338"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0339","@type":"sc:Canvas","label":"Hollar_a_3000_0339","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0339/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0339","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2682,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0339/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2682,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0339","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0339"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0340","@type":"sc:Canvas","label":"Hollar_a_3000_0340","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0340/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0340","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3981,"width":2813,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0340/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3981,"width":2813,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0340","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0340"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0341","@type":"sc:Canvas","label":"Hollar_a_3000_0341","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0341/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0341","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2723,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0341/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2723,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0341","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0341"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0342","@type":"sc:Canvas","label":"Hollar_a_3000_0342","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0342/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0342","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2795,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0342/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2795,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0342","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0342"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0343","@type":"sc:Canvas","label":"Hollar_a_3000_0343","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0343/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0343","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2705,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0343/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2705,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0343","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0343"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0344","@type":"sc:Canvas","label":"Hollar_a_3000_0344","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0344/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0344","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2861,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0344/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2861,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0344","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0344"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0345","@type":"sc:Canvas","label":"Hollar_a_3000_0345","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0345/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0345","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2670,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0345/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2670,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0345","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0345"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0346","@type":"sc:Canvas","label":"Hollar_a_3000_0346","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0346/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0346","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2867,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0346/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2867,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0346","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0346"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0347","@type":"sc:Canvas","label":"Hollar_a_3000_0347","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0347/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0347","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2699,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0347/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2699,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0347","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0347"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0348","@type":"sc:Canvas","label":"Hollar_a_3000_0348","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0348/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0348","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2849,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0348/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2849,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0348","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0348"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0349","@type":"sc:Canvas","label":"Hollar_a_3000_0349","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0349/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0349","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2699,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0349/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2699,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0349","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0349"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0350","@type":"sc:Canvas","label":"Hollar_a_3000_0350","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0350/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0350","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2873,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0350/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2873,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0350","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0350"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0351","@type":"sc:Canvas","label":"Hollar_a_3000_0351","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0351/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0351","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2664,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0351/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2664,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0351","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0351"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0352","@type":"sc:Canvas","label":"Hollar_a_3000_0352","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0352/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0352","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2843,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0352/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2843,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0352","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0352"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0353","@type":"sc:Canvas","label":"Hollar_a_3000_0353","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0353/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0353","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2652,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0353/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2652,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0353","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0353"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0354","@type":"sc:Canvas","label":"Hollar_a_3000_0354","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0354/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0354","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3981,"width":2885,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0354/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3981,"width":2885,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0354","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0354"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0355","@type":"sc:Canvas","label":"Hollar_a_3000_0355","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0355/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0355","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2610,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0355/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2610,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0355","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0355"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0356","@type":"sc:Canvas","label":"Hollar_a_3000_0356","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0356/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0356","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2723,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0356/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2723,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0356","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0356"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0357","@type":"sc:Canvas","label":"Hollar_a_3000_0357","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0357/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0357","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2765,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0357/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2765,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0357","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0357"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0358","@type":"sc:Canvas","label":"Hollar_a_3000_0358","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0358/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0358","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2759,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0358/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2759,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0358","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0358"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0359","@type":"sc:Canvas","label":"Hollar_a_3000_0359","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0359/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0359","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2801,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0359/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2801,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0359","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0359"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0360","@type":"sc:Canvas","label":"Hollar_a_3000_0360","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0360/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0360","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2783,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0360/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2783,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0360","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0360"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0361","@type":"sc:Canvas","label":"Hollar_a_3000_0361","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0361/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0361","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3987,"width":2789,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0361/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3987,"width":2789,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0361","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0361"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0362","@type":"sc:Canvas","label":"Hollar_a_3000_0362","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0362/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0362","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3998,"width":2807,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0362/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3998,"width":2807,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0362","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0362"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0363","@type":"sc:Canvas","label":"Hollar_a_3000_0363","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0363/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0363","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3969,"width":2724,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0363/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3969,"width":2724,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0363","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0363"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0364","@type":"sc:Canvas","label":"Hollar_a_3000_0364","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0364/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0364","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2813,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0364/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2813,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0364","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0364"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0365","@type":"sc:Canvas","label":"Hollar_a_3000_0365","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0365/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0365","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3993,"width":2705,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0365/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3993,"width":2705,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0365","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0365"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0366","@type":"sc:Canvas","label":"Hollar_a_3000_0366","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0366/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0366","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3995,"width":2843,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0366/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3995,"width":2843,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0366","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0366"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0367","@type":"sc:Canvas","label":"Hollar_a_3000_0367","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0367/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0367","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3950,"width":2615,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0367/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3950,"width":2615,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0367","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0367"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0368","@type":"sc:Canvas","label":"Hollar_a_3000_0368","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0368/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0368","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3995,"width":2813,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0368/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3995,"width":2813,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0368","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0368"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0369","@type":"sc:Canvas","label":"Hollar_a_3000_0369","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0369/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0369","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2717,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0369/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2717,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0369","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0369"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0370","@type":"sc:Canvas","label":"Hollar_a_3000_0370","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0370/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0370","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4007,"width":2729,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0370/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4007,"width":2729,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0370","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0370"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0371","@type":"sc:Canvas","label":"Hollar_a_3000_0371","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0371/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0371","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4012,"width":2729,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0371/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4012,"width":2729,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0371","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0371"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0372","@type":"sc:Canvas","label":"Hollar_a_3000_0372","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0372/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0372","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2801,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0372/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2801,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0372","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0372"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0373","@type":"sc:Canvas","label":"Hollar_a_3000_0373","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0373/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0373","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2741,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0373/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2741,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0373","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0373"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0374","@type":"sc:Canvas","label":"Hollar_a_3000_0374","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0374/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0374","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4007,"width":2777,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0374/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4007,"width":2777,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0374","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0374"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0375","@type":"sc:Canvas","label":"Hollar_a_3000_0375","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0375/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0375","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4007,"width":2699,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0375/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4007,"width":2699,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0375","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0375"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0376","@type":"sc:Canvas","label":"Hollar_a_3000_0376","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0376/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0376","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3995,"width":2855,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0376/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3995,"width":2855,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0376","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0376"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0377","@type":"sc:Canvas","label":"Hollar_a_3000_0377","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0377/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0377","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2664,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0377/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2664,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0377","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0377"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0378","@type":"sc:Canvas","label":"Hollar_a_3000_0378","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0378/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0378","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4007,"width":2789,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0378/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4007,"width":2789,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0378","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0378"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0379","@type":"sc:Canvas","label":"Hollar_a_3000_0379","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0379/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0379","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4007,"width":2711,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0379/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4007,"width":2711,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0379","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0379"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0380","@type":"sc:Canvas","label":"Hollar_a_3000_0380","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0380/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0380","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3906,"width":2767,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0380/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3906,"width":2767,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0380","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0380"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0381","@type":"sc:Canvas","label":"Hollar_a_3000_0381","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0381/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0381","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3940,"width":2586,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0381/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3940,"width":2586,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0381","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0381"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0382","@type":"sc:Canvas","label":"Hollar_a_3000_0382","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0382/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0382","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3942,"width":2802,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0382/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3942,"width":2802,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0382","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0382"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0383","@type":"sc:Canvas","label":"Hollar_a_3000_0383","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0383/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0383","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3995,"width":2723,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0383/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3995,"width":2723,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0383","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0383"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0384","@type":"sc:Canvas","label":"Hollar_a_3000_0384","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0384/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0384","profile":"http://iiif.io/api/image/2/level2.json"}},"height":3935,"width":2784,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0384/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":3935,"width":2784,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0384","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0384"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0385","@type":"sc:Canvas","label":"Hollar_a_3000_0385","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0385/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0385","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4012,"width":2741,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0385/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4012,"width":2741,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0385","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0385"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0386","@type":"sc:Canvas","label":"Hollar_a_3000_0386","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0386/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0386","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4012,"width":2777,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0386/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4012,"width":2777,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0386","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0386"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0387","@type":"sc:Canvas","label":"Hollar_a_3000_0387","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0387/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0387","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4012,"width":2747,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0387/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4012,"width":2747,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0387","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0387"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0388","@type":"sc:Canvas","label":"Hollar_a_3000_0388","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0388/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0388","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2783,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0388/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2783,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0388","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0388"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0389","@type":"sc:Canvas","label":"Hollar_a_3000_0389","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0389/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0389","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4007,"width":2759,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0389/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4007,"width":2759,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0389","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0389"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0390","@type":"sc:Canvas","label":"Hollar_a_3000_0390","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0390/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0390","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2909,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0390/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2909,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0390","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0390"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0391","@type":"sc:Canvas","label":"Hollar_a_3000_0391","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0391/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0391","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4001,"width":2771,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0391/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4001,"width":2771,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0391","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0391"}]},{"@id":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0392","@type":"sc:Canvas","label":"Hollar_a_3000_0392","thumbnail":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0392/full/80,/0/default.jpg","service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0392","profile":"http://iiif.io/api/image/2/level2.json"}},"height":4096,"width":2852,"images":[{"@type":"oa:Annotation","motivation":"sc:Painting","resource":{"@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0392/full/full/0/default.jpg","@type":"dctypes:Image","format":"image/jpg","height":4096,"width":2852,"service":{"@context":"http://iiif.io/api/image/2/context.json","@id":"https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0392","profile":"http://iiif.io/api/image/2/level2.json"}},"on":"https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/canvas/hollar:Hollar_a_3000_0392"}]}]}]} \ No newline at end of file diff --git a/viscoll-api/spec/fixtures/villanova_boston.json b/viscoll-api/spec/fixtures/villanova_boston.json new file mode 100644 index 00000000..16ba1032 --- /dev/null +++ b/viscoll-api/spec/fixtures/villanova_boston.json @@ -0,0 +1,93 @@ +{ + "@context": "http://iiif.io/api/presentation/2/context.json", + "@type": "sc:Manifest", + "@id": "https://digital.library.villanova.edu/Item/vudl:99213/Manifest", + "label": "Boston, and Bunker Hill. Provided by Villanova University.", + "metadata": [ + { + "label": "Full Title", + "value": "Boston, and Bunker Hill." + }, + { + "label": "Author", + "value": "Bartlett, W.H." + }, + { + "label": "Contributor", + "value": "Cousen, C." + }, + { + "label": "Date Added", + "value": "12 January 2014" + }, + { + "label": "Language", + "value": "English" + }, + { + "label": "Topic", + "value": "Prints > 19th century.
cultural landscapes.
Engravings.
Landscape prints.
Cityscape prints
Views > Massachusetts > Boston.
History > United States > Massachusetts.
Rivers.
Waterfronts.
Bridges.
Sailing ships.
Livestock.
" + }, + { + "label": "About", + "value": "More Details
Permanent Link
" + } + ], + "description": "

Image from Patrick Coad Family Papers. These materials are owned by the American Catholic Historical Society and maintained at the Philadelphia Archdiocesan Historical Research Center, 100 E. Wynnewood Rd. Wynnewood, PA 19096. For more information please see: http://www.pahrc.net.

", + "license": "http://creativecommons.org/licenses/by-nc-nd/3.0/", + "attribution": "Digital Library@Villanova University", + "related": { + "@id": "https://digital.library.villanova.edu/Item/vudl:99213", + "format": "text/html" + }, + "within": "https://digital.library.villanova.edu/Collection/vudl:98245/IIIF", + "sequences": [ + { + "@type": "sc:Sequence", + "label": "Pages", + "rendering": [], + "viewingDirection": "left-to-right", + "viewingHint": "paged", + "canvases": [ + { + "@type": "sc:Canvas", + "@id": "https://digital.library.villanova.edu/Item/vudl:99213/Canvas/p0", + "label": null, + "rendering": [ + { + "@id": "https://digital.library.villanova.edu/files/vudl:99215/MASTER", + "format": "image/tiff", + "label": "Original source file" + }, + { + "@id": "https://digital.library.villanova.edu/files/vudl:99215/MASTER-MD", + "format": "application/xml", + "label": "Technical Metadata" + } + ], + "height": 3288, + "width": 4260, + "images": [ + { + "@type": "oa:Annotation", + "motivation": "sc:painting", + "resource": { + "@id": "https://digital.library.villanova.edu/files/vudl:99215/LARGE", + "@type": "dctypes:Image", + "format": "image/jpeg", + "service": { + "@context": "http://iiif.io/api/image/2/context.json", + "@id": "https://iiif.library.villanova.edu/image/vudl%3A99215", + "profile": "http://iiif.io/api/image/2/level1.json" + }, + "height": 3288, + "width": 4260 + }, + "on": "https://digital.library.villanova.edu/Item/vudl:99213/Canvas/p0" + } + ] + } + ] + } + ] +} diff --git a/viscoll-api/spec/fixtures/viscoll.png b/viscoll-api/spec/fixtures/viscoll.png new file mode 100644 index 00000000..b31c0248 Binary files /dev/null and b/viscoll-api/spec/fixtures/viscoll.png differ diff --git a/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb new file mode 100644 index 00000000..adfac092 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb @@ -0,0 +1,125 @@ +require 'rails_helper' + +RSpec.describe ControllerHelper::ExportHelper, type: :helper do + before do + stub_request(:get, 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/villanova_boston.json'), headers: {}) + @project = FactoryGirl.create(:project, + 'title' => 'Sample project', + 'shelfmark' => 'Ravenna 384.2339', + 'metadata' => { date: '18th century' }, + 'preferences' => { 'showTips' => true }, + 'noteTypes' => ['Ink', 'Unknown'], + 'manifests' => { '12341234': { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } } + ) + # Attach group with 2 leafs - (group with 2 leafs) - 2 conjoined leafs + @testgroup = FactoryGirl.create(:group, project: @project, nestLevel: 1, title: 'Group 1', direction: "left-to-right") + @upleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testgroup.id.to_s, nestLevel: 1) } + @testmidgroup = FactoryGirl.create(:group, project: @project, parentID: @testgroup.id.to_s, nestLevel: 2, title: 'Group 2', direction: "left-to-right") + @midleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testmidgroup.id.to_s, nestLevel: 2) } + @botleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testgroup.id.to_s, nestLevel: 1) } + @botleafs[1].update(type: 'Endleaf') + @project.add_groupIDs([@testgroup.id.to_s, @testmidgroup.id.to_s], 0) + @testgroup.add_members([@upleafs[0].id.to_s, @upleafs[1].id.to_s, @testmidgroup.id.to_s, @botleafs[0].id.to_s, @botleafs[1].id.to_s], 0) + @testmidgroup.add_members([@midleafs[0].id.to_s, @midleafs[1].id.to_s], 0) + @testnote = FactoryGirl.create(:note, project: @project, title: 'Test Note', type: 'Ink', description: 'This is a test', show: true, objects: {Group: [@testgroup.id.to_s], Leaf: [@botleafs[0].id.to_s], Recto: [@botleafs[0].rectoID], Verso: [@botleafs[0].versoID]}) + end + + it 'builds the right JSON' do + result = buildJSON(@project) + expect(result[:project]).to eq({ + title: 'Sample project', + shelfmark: 'Ravenna 384.2339', + metadata: { 'date' => '18th century' }, + preferences: { 'showTips' => true }, + manifests: { '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }, + noteTypes: ['Ink', 'Unknown'] + }) + expect(result[:groups]).to eq({ + 1 => {:params=>{:type=>"Quire", :title=>"Group 1", :direction=>"left-to-right", :nestLevel=>1}, :tacketed=>[], :sewing=>[], :parentOrder=>nil, :memberOrders=>["Leaf_1", "Leaf_2", "Group_2", "Leaf_5", "Leaf_6"]}, + 2 => {:params=>{:type=>"Quire", :title=>"Group 2", :direction=>"left-to-right", :nestLevel=>2}, :tacketed=>[], :sewing=>[], :parentOrder=>1, :memberOrders=>["Leaf_3", "Leaf_4"]} + }) + expect(result[:leafs]).to eq({ + 1 => {:params=>{:material=>"Paper", :type=>"Original", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>1}, :conjoined_leaf_order=>nil, :parentOrder=>1, :rectoOrder=>1, :versoOrder=>1}, + 2 => {:params=>{:material=>"Paper", :type=>"Original", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>1}, :conjoined_leaf_order=>nil, :parentOrder=>1, :rectoOrder=>2, :versoOrder=>2}, + 3 => {:params=>{:material=>"Paper", :type=>"Original", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>2}, :conjoined_leaf_order=>nil, :parentOrder=>2, :rectoOrder=>3, :versoOrder=>3}, + 4 => {:params=>{:material=>"Paper", :type=>"Original", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>2}, :conjoined_leaf_order=>nil, :parentOrder=>2, :rectoOrder=>4, :versoOrder=>4}, + 5 => {:params=>{:material=>"Paper", :type=>"Original", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>1}, :conjoined_leaf_order=>nil, :parentOrder=>1, :rectoOrder=>5, :versoOrder=>5}, + 6 => {:params=>{:material=>"Paper", :type=>"Endleaf", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>1}, :conjoined_leaf_order=>nil, :parentOrder=>1, :rectoOrder=>6, :versoOrder=>6} + }) + expect(result[:rectos]).to eq({ + 1 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>1}, + 2 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>2}, + 3 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>3}, + 4 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>4}, + 5 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>5}, + 6 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>6} + }) + expect(result[:versos]).to eq({ + 1 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>1}, + 2 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>2}, + 3 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>3}, + 4 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>4}, + 5 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>5}, + 6 => {:params=>{:folio_number=>"", :page_number=>"", :texture=>"None", :image=>{}, :script_direction=>"None"}, :parentOrder=>6} + }) + expect(result[:notes]).to eq({ + 1 => {:params=>{:title=>"Test Note", :type=>"Ink", :description=>"This is a test", :show=>true}, :objects=>{:Group=>[1], :Leaf=>[5], :Recto=>[5], :Verso=>[5]}} + }) + end + + it 'builds the right XML' do + result = Nokogiri::XML(buildDotModel(@project)) + # Metadata elements + expect(result.css("manuscript title").text).to eq 'Sample project' + expect(result.css("manuscript shelfmark").text).to eq 'Ravenna 384.2339' + expect(result.css("manuscript date").text).to eq '18th century' + expect(result.css("taxonomy[xml|id='manuscript_preferences'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['manuscript_preferences_ravenna_384_2339_showTips', 'true'] + ) + expect(result.css("taxonomy[xml|id='manifests'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['manifest_12341234', 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest'] + ) + # Quires + expect(result.css("taxonomy[xml|id='group_type'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['group_type_quire', 'Quire'] + ) + expect(result.css("taxonomy[xml|id='group_title'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['group_title_group_1', 'Group 1'], + ['group_title_group_2', 'Group 2'], + ) + expect(result.css("taxonomy[xml|id='group_members'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['group_members_ravenna_384_2339-q-1', '#ravenna_384_2339-1-1 #ravenna_384_2339-1-2 #ravenna_384_2339-q-1-2 #ravenna_384_2339-1-3 #ravenna_384_2339-1-4'], + ['group_members_ravenna_384_2339-q-1-2', '#ravenna_384_2339-1-2-3 #ravenna_384_2339-1-2-4'], + ) + # Leaves + expect(result.css("taxonomy[xml|id='leaf_material'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['leaf_material_paper', 'Paper'] + ) + expect(result.css("manuscript leaf").collect { |t| [t['xml:id'], t.css('folioNumber').first.text, t.css('q').first['target'], t.css('q').first['n']] }).to include( + ['ravenna_384_2339-1-1', '1', '#ravenna_384_2339-q-1', '1'], + ['ravenna_384_2339-1-2', '2', '#ravenna_384_2339-q-1', '1'], + ['ravenna_384_2339-1-2-3', '3', '#ravenna_384_2339-q-1-2', '2'], + ['ravenna_384_2339-1-2-4', '4', '#ravenna_384_2339-q-1-2', '2'], + ['ravenna_384_2339-1-3', '5', '#ravenna_384_2339-q-1', '1'], + ['ravenna_384_2339-1-4', '6', '#ravenna_384_2339-q-1', '1'] + ) + # Sides and Notes + expect(result.css("mapping map").collect { |t| [t['target'], t['side'], t.css('term').first['target']]}).to include( + ['#ravenna_384_2339-1-1', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-3', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-4', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-3', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-4', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-1', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-3', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-4', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-3', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-4', 'verso', '#side_page_number_EMPTY'] + ) + expect(result.css("mapping map").collect { |t| [t['target'], t.css('term').first['target']]}).to include( + ['#ravenna_384_2339-n-1', '#note_title_test_note #note_show'], + ) + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb new file mode 100644 index 00000000..240b6000 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb @@ -0,0 +1,182 @@ +require 'rails_helper' + +RSpec.describe ControllerHelper::FilterHelper, type: :helper do + it 'should reject empty queries' do + expect(runValidations([])).to eq ['should contain at least 1 query'] + end + + it 'should reject unrecognized types' do + expect(runValidations([{ 'type' => 'foobar' }])).to include a_hash_including('type' => 'type should be one of: [group, leaf, side, note]') + end + + it 'should reject unrecognized conjunctions' do + result = runValidations([ + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'equals', 'values' => ['Codex Taorminae 1'] }, + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'equals', 'values' => ['Codex Taorminae 2'], 'conjunction' => 'XOR' }, + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'equals', 'values' => ['Codex Taorminae 3'] } + ]) + expect(result).to include a_hash_including('conjunction' => 'conjunction should be one of : [AND, OR]') + end + + it 'should reject empty query values' do + result = runValidations([ + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'equals', 'values' => [] }, + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'equals', 'values' => ['Codex Taorminae 2'], 'conjunction' => 'OR' }, + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'equals', 'values' => ['Codex Taorminae 3'] } + ]) + expect(result).to include a_hash_including('values' => 'query value cannot be empty') + end + + describe 'Group queries' do + it 'should reject invalid attribute for groups' do + result = runValidations([ + { 'type' => 'group', 'attribute' => 'waahoo', 'condition' => 'waahoo', 'values' => ['Quire'] } + ]) + expect(result).to include a_hash_including('attribute' => 'valid attributes for group: [type, title]') + end + + it 'should accept valid parameters for type' do + ['equals', 'not equals'].each do |op| + result = runValidations([ + { 'type' => 'group', 'attribute' => 'type', 'condition' => op, 'values' => ['Quire'] } + ]) + expect(result).to be_empty + end + end + + it 'should reject invalid conditions for type' do + result = runValidations([ + { 'type' => 'group', 'attribute' => 'type', 'condition' => 'contains', 'values' => ['Quire'] } + ]) + expect(result).to include a_hash_including('condition' => 'valid conditions for group attribute type : [equals, not equals]') + end + + it 'should accept valid parameters for title' do + ['equals', 'not equals', 'contains', 'not contains'].each do |op| + result = runValidations([ + { 'type' => 'group', 'attribute' => 'title', 'condition' => op, 'values' => ['Codex Taorminae'] } + ]) + expect(result).to be_empty + end + end + + it 'should reject invalid conditions for title' do + result = runValidations([ + { 'type' => 'group', 'attribute' => 'title', 'condition' => 'waahoo', 'values' => ['Codex Taorminae'] } + ]) + expect(result).to include a_hash_including('condition' => "valid conditions for group attribute title : [equals, not equals, contains, not contains]") + end + end + + describe 'Leaf queries' do + it 'should reject invalid attribute for leafs' do + result = runValidations([ + { 'type' => 'leaf', 'attribute' => 'waahoo', 'condition' => 'equals', 'values' => ['3'] } + ]) + expect(result).to include a_hash_including('attribute' => 'valid attributes for leaf: [type, material, conjoined_to, conjoined_leaf_order, attached_above, attached_below, stub]') + end + + it 'should accept valid parameters for conditions' do + ['type', 'material', 'conjoined_to', 'conjoined_leaf_order', 'attached_above', 'attached_below', 'stub'].each do |attribute| + result = runValidations([ + { 'type' => 'leaf', 'attribute' => attribute, 'condition' => 'eq', 'values' => ['Some Value'] } + ]) + expect(result).to include a_hash_including('condition' => "valid conditions for leaf attribute #{attribute} : [equals, not equals]") + end + end + end + + describe 'Side queries' do + it 'should reject invalid attribute for sides' do + result = runValidations([ + { 'type' => 'side', 'attribute' => 'waahoo', 'condition' => 'equals', 'values' => ['3r'] } + ]) + expect(result).to include a_hash_including('attribute' => 'valid attributes for side: [folio_number, page_number, texture, script_direction, uri]') + end + + it 'should reject invalid conditions for texture and script_direction' do + ['texture', 'script_direction'].each do |attribute| + result = runValidations([ + { 'type' => 'side', 'attribute' => attribute, 'condition' => 'waahoo', 'values' => ['value'] } + ]) + expect(result).to include a_hash_including('condition' => "valid conditions for side attribute #{attribute} : [equals, not equals]") + end + end + + it 'should accept valid conditions for texture and script_direction' do + ['texture', 'script_direction'].each do |attribute| + ['equals', 'not equals'].each do |condition| + result = runValidations([ + { 'type' => 'side', 'attribute' => attribute, 'condition' => condition, 'values' => ['value'] } + ]) + expect(result).to be_empty + end + end + end + + it 'should reject invalid conditions for folio_number and uri' do + ['folio_number', 'uri'].each do |attribute| + result = runValidations([ + { 'type' => 'side', 'attribute' => attribute, 'condition' => 'waahoo', 'values' => ['value'] } + ]) + expect(result).to include a_hash_including('condition' => "valid conditions for side attribute #{attribute} : [equals, not equals, contains, not contains]") + end + end + + it 'should accept valid conditions for folio_number and uri' do + ['folio_number', 'uri'].each do |attribute| + ['equals', 'not equals', 'contains', 'not contains'].each do |condition| + result = runValidations([ + { 'type' => 'side', 'attribute' => attribute, 'condition' => condition, 'values' => ['value'] } + ]) + expect(result).to be_empty + end + end + end + end + + describe 'Note queries' do + it 'should reject invalid attribute for sides' do + result = runValidations([ + { 'type' => 'note', 'attribute' => 'waahoo', 'condition' => 'equals', 'values' => ['hint'] } + ]) + expect(result).to include a_hash_including('attribute' => 'valid attributes for note: [title, type, description]') + end + + it 'should reject invalid conditions for type' do + result = runValidations([ + { 'type' => 'note', 'attribute' => 'type', 'condition' => 'waahoo', 'values' => ['hint'] } + ]) + expect(result).to include a_hash_including('condition' => 'valid conditions for note attribute type : [equals, not equals]') + end + + it 'should accept valid conditions for type' do + ['equals', 'not equals'].each do |condition| + result = runValidations([ + { 'type' => 'note', 'attribute' => 'type', 'condition' => condition, 'values' => ['hint'] } + ]) + expect(result).to be_empty + end + end + + it 'should reject invalid conditions for title and description' do + ['title', 'description'].each do |attribute| + result = runValidations([ + { 'type' => 'note', 'attribute' => attribute, 'condition' => 'waahoo', 'values' => ['hint'] } + ]) + expect(result).to include a_hash_including('condition' => "valid conditions for note attribute #{attribute} : [equals, not equals, contains, not contains]") + end + end + + it 'should accept valid conditions for title and description' do + ['title', 'description'].each do |attribute| + ['equals', 'not equals', 'contains', 'not contains'].each do |condition| + result = runValidations([ + { 'type' => 'note', 'attribute' => attribute, 'condition' => condition, 'values' => ['hint'] } + ]) + expect(result).to be_empty + end + end + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb new file mode 100644 index 00000000..ff7051d5 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb @@ -0,0 +1,25 @@ +require 'rails_helper' + +RSpec.describe ControllerHelper::GroupsHelper, type: :helper do + before :each do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:group, project: @project) + end + + describe 'addLeavesInside' do + it 'adds unconjoined leaves' do + addLeavesInside(@project.id.to_s, @group, 4, false, nil) + expect(@project.leafs.count).to eq 4 + expect(@group.memberIDs.count).to eq 4 + expect(@project.leafs.all? { |leaf| leaf.conjoined_to.blank? }).to be true + end + it 'adds conjoined leaves' do + addLeavesInside(@project.id.to_s, @group, 4, true, nil) + expect(@project.leafs.count).to eq 4 + expect(@group.memberIDs.count).to eq 4 + 4.times.each do |i| + expect(@project.leafs[i].conjoined_to).to eq @project.leafs[3-i].id.to_s + end + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb new file mode 100644 index 00000000..c03f37a3 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb @@ -0,0 +1,100 @@ +require 'rails_helper' + +module ControllerHelper + module StubbedImportHelper + include ControllerHelper::ImportHelper + + def current_user + User.last + end + end +end + +RSpec.describe ControllerHelper::StubbedImportHelper, type: :helper do + describe 'JSON Import' do + let(:json_import_data) do + { + "project" => { + "title" => 'Sample project', + "shelfmark" => 'Ravenna 384.2339', + "metadata" => { 'date' => '18th century' }, + "preferences" => { 'showTips' => true }, + "manifests" => { '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }, + "noteTypes" => ['Hand', 'Ink', 'Unknown'] + }, + "Groups" => { + "1" => {"params" => {"type" => "Quire", "title" => "Quire 1", "nestLevel" => 1}, "tacketed" => [], "sewing" => [], "parentOrder" => nil, "memberOrders" => ["Leaf_1", "Leaf_2", "Group_2", "Leaf_5", "Leaf_6"]}, + "2" => {"params" => {"type" => "Quire", "title" => "Quire 2", "nestLevel" => 2}, "tacketed" => [], "sewing" => [], "parentOrder" => 1, "memberOrders" => ["Leaf_3", "Leaf_4"]} + }, + "Leafs" => { + "1" => {"params" => {"material" => "Paper", "type" => "Original", "attached_above" => "None", "attached_below" => "None", "stub" => "None", "nestLevel" => 1}, "conjoined_leaf_order" => nil, "parentOrder" => 1, "rectoOrder" => 1, "versoOrder" => 1}, + "2" => {"params" => {"material" => "Paper", "type" => "Original", "attached_above" => "None", "attached_below" => "None", "stub" => "None", "nestLevel" => 1}, "conjoined_leaf_order" => nil, "parentOrder" => 1, "rectoOrder" => 2, "versoOrder" => 2}, + "3" => {"params" => {"material" => "Paper", "type" => "Original", "attached_above" => "None", "attached_below" => "None", "stub" => "None", "nestLevel" => 2}, "conjoined_leaf_order" => nil, "parentOrder" => 2, "rectoOrder" => 3, "versoOrder" => 3}, + "4" => {"params" => {"material" => "Paper", "type" => "Original", "attached_above" => "None", "attached_below" => "None", "stub" => "None", "nestLevel" => 2}, "conjoined_leaf_order" => nil, "parentOrder" => 2, "rectoOrder" => 4, "versoOrder" => 4}, + "5" => {"params" => {"material" => "Paper", "type" => "Original", "attached_above" => "None", "attached_below" => "None", "stub" => "None", "nestLevel" => 1}, "conjoined_leaf_order" => nil, "parentOrder" => 1, "rectoOrder" => 5, "versoOrder" => 5}, + "6" => {"params" => {"material" => "Paper", "type" => "Endleaf", "attached_above" => "None", "attached_below" => "None", "stub" => "None", "nestLevel" => 1}, "conjoined_leaf_order" => nil, "parentOrder" => 1, "rectoOrder" => 6, "versoOrder" => 6} + }, + "Rectos" => { + "1" => {"params" => {"folio_number" => "1R", "texture" => "Hair", "image" => {}, "script_direction" => "None"}, "parentOrder" => 1}, + "2" => {"params" => {"folio_number" => "2R", "texture" => "Hair", "image" => {}, "script_direction" => "None"}, "parentOrder" => 2}, + "3" => {"params" => {"folio_number" => "3R", "texture" => "Hair", "image" => {}, "script_direction" => "None"}, "parentOrder" => 3}, + "4" => {"params" => {"folio_number" => "4R", "texture" => "Hair", "image" => {}, "script_direction" => "None"}, "parentOrder" => 4}, + "5" => {"params" => {"folio_number" => "5R", "texture" => "Hair", "image" => {}, "script_direction" => "None"}, "parentOrder" => 5}, + "6" => {"params" => {"folio_number" => "6R", "texture" => "Hair", "image" => {}, "script_direction" => "None"}, "parentOrder" => 6} + }, + "Versos" => { + "1" => {"params" => {"folio_number" => "1V", "texture" => "Flesh", "image" => {}, "script_direction" => "None"}, "parentOrder" => 1}, + "2" => {"params" => {"folio_number" => "2V", "texture" => "Flesh", "image" => {}, "script_direction" => "None"}, "parentOrder" => 2}, + "3" => {"params" => {"folio_number" => "3V", "texture" => "Flesh", "image" => {}, "script_direction" => "None"}, "parentOrder" => 3}, + "4" => {"params" => {"folio_number" => "4V", "texture" => "Flesh", "image" => {}, "script_direction" => "None"}, "parentOrder" => 4}, + "5" => {"params" => {"folio_number" => "5V", "texture" => "Flesh", "image" => {}, "script_direction" => "None"}, "parentOrder" => 5}, + "6" => {"params" => {"folio_number" => "6V", "texture" => "Flesh", "image" => {}, "script_direction" => "None"}, "parentOrder" => 6} + }, + "Notes" => { + "1" => {"params" => {"title" => "Test Note", "type" => "Ink", "description" => "This is a test", "show" => true}, "objects" => {"Group" => [1], "Leaf" => [5], "Recto" => [5], "Verso" => [5]}} + } + } + end + + it 'should import properly' do + user = FactoryGirl.create(:user) + expect{ handleJSONImport(json_import_data) }.to change{Project.count}.by(1) + project = Project.last + expect(project.title).to eq 'Sample project' + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to eq ['Hand', 'Ink', 'Unknown'] + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + + it 'should avoid overwriting a project of the same name' do + user = FactoryGirl.create(:user) + existing_project = FactoryGirl.create(:project, title: 'Ultra waahoo project is ultra waahoo') + duplicated_data = json_import_data + duplicated_data['project']['title'] = existing_project.title + expect{ handleJSONImport(duplicated_data) }.to change{Project.count}.by(1) + existing_project.reload + expect(existing_project.title).to eq 'Ultra waahoo project is ultra waahoo' + project = Project.last + expect(project.title[0..46]).to eq "Copy of Ultra waahoo project is ultra waahoo @ " + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to eq ['Hand', 'Ink', 'Unknown'] + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/import_json_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/import_json_helper_spec.rb new file mode 100644 index 00000000..b110da90 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/import_json_helper_spec.rb @@ -0,0 +1,60 @@ +require 'rails_helper' + +module ControllerHelper + module StubbedImportHelper + include ControllerHelper::ImportJsonHelper + + def current_user + User.last + end + end +end + +RSpec.describe ControllerHelper::StubbedImportHelper, type: :helper do + describe 'JSON Import' do + let(:json_import_data) do + JSON.parse(File.open(File.dirname(__FILE__) + '/../../fixtures/sample_import_json.json', 'r') { |file| file.read }) + end + + it 'should import properly' do + user = FactoryGirl.create(:user) + expect{ handleJSONImport(json_import_data) }.to change{Project.count}.by(1) + project = Project.last + expect(project.title).to eq 'Sample project' + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to eq ['Hand', 'Ink', 'Unknown'] + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + + it 'should avoid overwriting a project of the same name' do + user = FactoryGirl.create(:user) + existing_project = FactoryGirl.create(:project, title: 'Ultra waahoo project is ultra waahoo') + duplicated_data = json_import_data + duplicated_data['project']['title'] = existing_project.title + expect{ handleJSONImport(duplicated_data) }.to change{Project.count}.by(1) + existing_project.reload + expect(existing_project.title).to eq 'Ultra waahoo project is ultra waahoo' + project = Project.last + expect(project.title[0..46]).to eq "Copy of Ultra waahoo project is ultra waahoo @ " + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to eq ['Hand', 'Ink', 'Unknown'] + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/import_mapping_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/import_mapping_helper_spec.rb new file mode 100644 index 00000000..524f57cb --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/import_mapping_helper_spec.rb @@ -0,0 +1,68 @@ +require 'rails_helper' + +RSpec.describe ControllerHelper::ImportMappingHelper, type: :helper do + before do + @base_api_url = 'http://127.0.0.1:12345' + @user = FactoryGirl.create(:user) + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1,3]]) + end + + after do + Image.where(:projectIDs => @project.id.to_s).each do | image | + image.destroy + end + end + + describe 'handleMappingImport' do + it 'should run properly with images in various attachment situations' do + # Prep user with preloaded images + preloads = [ + FactoryGirl.create(:image, user: @user, projectIDs: [@project.id.to_s], filename: '1V.png', fileID: '5a28221ec199860e7a2f5fd1'), + FactoryGirl.create(:image, user: @user, projectIDs: [@project.id.to_s], filename: '2R.png', fileID: '5a28221ec199860e7a1shibainu', id: '5a28221ec199860e7a2f5fd1shibainu'), + FactoryGirl.create(:image, user: @user, projectIDs: [@project.id.to_s], filename: '2V.png', fileID: '0e7a2f5fd1waahoo', id: '5a28221ec199860e7a2f5fd1waahoo') + ] + @user.images = preloads + @user.save + # Situation 1: Brand new image uploaded + @project.sides[0].update(image: { + manifestID: 'DIYImages', + label: '1R', + url: 'http://www.foobar.net/images/5a28221ec199860e7a2f5fd1_1R.png' + }) + # Situation 2: Uploaded image same name and content as existing image + @project.sides[1].update(image: { + manifestID: 'DIYImages', + label: '1V', + url: 'http://www.foobar.net/images/5a28221ec199860e7a2f5fd1_1V.png' + }) + # Situation 3: Uploaded image same name but different content from existing image + @project.sides[2].update(image: { + manifestID: 'DIYImages', + label: '2R', + url: 'http://www.foobar.net/images/5a28221ec199860e7a2f5fd1_2R.png' + }) + # Situation 4: Image exists with current user but not uploaded + @project.sides[3].update(image: { + manifestID: 'DIYImages', + label: '2V', + url: 'http://www.foobar.net/images/5a28221ec199860e7a2f5fd1waahoo_2V.png' + }) + # Situation 5: Image specified but not uploaded + @project.sides[4].update(image: { + manifestID: 'DIYImages', + label: '3R', + url: 'http://www.foobar.net/images/5a28221ec199860e7a2f5fd1_3R.png' + }) + zipData = File.open("#{Rails.root}/spec/fixtures/base64zip.txt", "rb").read + handleMappingImport(@project, zipData, @user) + @project.reload + expect(@project.sides[0].image).to include('manifestID' => 'DIYImages') + expect(@project.sides[0].image['url']).to match(/http:\/\/127\.0\.0\.1:12345\/images\/[\w]+_1R\.png/) + expect(@project.sides[1].image).to include('manifestID' => 'DIYImages', 'url' => "http://127.0.0.1:12345/images/#{preloads[0].id}_1V.png") + expect(@project.sides[2].image).to include('manifestID' => 'DIYImages') + expect(@project.sides[2].image['url']).to match(/http:\/\/127\.0\.0\.1:12345\/images\/[\w]+_2R\(copy\)\.png/) + expect(@project.sides[3].image).to include('manifestID' => 'DIYImages', 'url' => "http://127.0.0.1:12345/images/5a28221ec199860e7a2f5fd1waahoo_2V.png") + expect(@project.sides[4].image).to be_empty + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/import_xml_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/import_xml_helper_spec.rb new file mode 100644 index 00000000..6220651f --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/import_xml_helper_spec.rb @@ -0,0 +1,61 @@ +require 'rails_helper' + +module ControllerHelper + module StubbedXmlImportHelper + include ControllerHelper::ImportXmlHelper + include ControllerHelper::ImportJsonHelper + + def current_user + User.last + end + end +end + +RSpec.describe ControllerHelper::StubbedXmlImportHelper, type: :helper do + describe 'XML Import' do + let(:xml_import_data) do + Nokogiri::XML(File.open(File.dirname(__FILE__) + '/../../fixtures/sample_import_xml.xml', 'r') { |file| file.read }) + end + + it 'should import properly' do + user = FactoryGirl.create(:user) + expect{ handleXMLImport(xml_import_data) }.to change{Project.count}.by(1) + project = Project.last + expect(project.title).to eq 'Sample project' + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to include('Ink', 'Unknown') + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + + it 'should avoid overwriting a project of the same name' do + user = FactoryGirl.create(:user) + existing_project = FactoryGirl.create(:project, title: 'Ultra waahoo project is ultra waahoo') + duplicated_data = xml_import_data + duplicated_data.at_css('viscoll manuscript title').content = existing_project.title + expect{ handleXMLImport(duplicated_data) }.to change{Project.count}.by(1) + existing_project.reload + expect(existing_project.title).to eq 'Ultra waahoo project is ultra waahoo' + project = Project.last + expect(project.title[0..46]).to eq "Copy of Ultra waahoo project is ultra waahoo @ " + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to include('Ink', 'Unknown') + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb new file mode 100644 index 00000000..9ed42ed6 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb @@ -0,0 +1,177 @@ +require 'rails_helper' + +RSpec.describe ControllerHelper::LeafsHelper, type: :helper do + describe 'autoConjoinLeaves' do + describe 'even leaves' do + before :each do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leaves = 4.times.collect { FactoryGirl.create(:leaf, project: @project) } + @group.add_members(@leaves.collect { |leaf| leaf.id.to_s }, 0) + @project.update({ leafs: @leaves }) + end + + it 'conjoins new leaves' do + autoConjoinLeaves(@leaves, nil) + @project.reload + @leaves = @project.leafs + expect(@leaves[0].conjoined_to).to eq @leaves[3].id.to_s + expect(@leaves[1].conjoined_to).to eq @leaves[2].id.to_s + expect(@leaves[2].conjoined_to).to eq @leaves[1].id.to_s + expect(@leaves[3].conjoined_to).to eq @leaves[0].id.to_s + end + + it 'reconfigures existing leaves' do + @leaves[0].conjoined_to = @leaves[1].id.to_s + @leaves[1].conjoined_to = @leaves[0].id.to_s + @leaves[2].conjoined_to = @leaves[3].id.to_s + @leaves[3].conjoined_to = @leaves[2].id.to_s + @leaves.each { |leaf| leaf.save } + autoConjoinLeaves(@leaves, nil) + @project.reload + @leaves = @project.leafs + expect(@leaves[0].conjoined_to).to eq @leaves[3].id.to_s + expect(@leaves[1].conjoined_to).to eq @leaves[2].id.to_s + expect(@leaves[2].conjoined_to).to eq @leaves[1].id.to_s + expect(@leaves[3].conjoined_to).to eq @leaves[0].id.to_s + end + end + + describe 'odd leaves' do + before :each do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leaves = 5.times.collect { FactoryGirl.create(:leaf, project: @project) } + @group.add_members(@leaves.collect { |leaf| leaf.id.to_s }, 0) + @project.update({ leafs: @leaves }) + end + + it 'conjoins new leaves' do + autoConjoinLeaves(@leaves, 2) + @project.reload + @leaves = @project.leafs + expect(@leaves[0].conjoined_to).to eq @leaves[4].id.to_s + expect(@leaves[1].conjoined_to).to be_blank + expect(@leaves[2].conjoined_to).to eq @leaves[3].id.to_s + expect(@leaves[3].conjoined_to).to eq @leaves[2].id.to_s + expect(@leaves[4].conjoined_to).to eq @leaves[0].id.to_s + end + + it 'reconfigures existing leaves' do + @leaves[0].conjoined_to = @leaves[1].id.to_s + @leaves[1].conjoined_to = @leaves[0].id.to_s + @leaves[3].conjoined_to = @leaves[4].id.to_s + @leaves[4].conjoined_to = @leaves[3].id.to_s + @leaves.each { |leaf| leaf.save } + autoConjoinLeaves(@leaves, 4) + @project.reload + @leaves = @project.leafs + expect(@leaves[0].conjoined_to).to eq @leaves[4].id.to_s + expect(@leaves[1].conjoined_to).to eq @leaves[2].id.to_s + expect(@leaves[2].conjoined_to).to eq @leaves[1].id.to_s + expect(@leaves[3].conjoined_to).to be_blank + expect(@leaves[4].conjoined_to).to eq @leaves[0].id.to_s + end + end + + describe 'reconjoin odd subleaves' do + before do + @project = FactoryGirl.create(:codex_project, quire_structure: [[1,8]]) + @leaves = @project.leafs + end + + it 'reconfigures leaves properly when conjoining first 5' do + expect(@leaves[2].conjoined_to).to eq @leaves[5].id.to_s + autoConjoinLeaves(@leaves[0..4], 3) + @project.reload + @leaves = @project.leafs + expect(@leaves[0].conjoined_to).to eq @leaves[4].id.to_s + expect(@leaves[1].conjoined_to).to eq @leaves[3].id.to_s + expect(@leaves[2].conjoined_to).to be_blank + expect(@leaves[3].conjoined_to).to eq @leaves[1].id.to_s + expect(@leaves[4].conjoined_to).to eq @leaves[0].id.to_s + expect(@leaves[5].conjoined_to).to be_blank + expect(@leaves[6].conjoined_to).to be_blank + expect(@leaves[7].conjoined_to).to be_blank + end + end + end + + describe 'update_attached_to' do + before :each do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leaves = 5.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @group.id.to_s) } + @group.add_members(@leaves.collect { |leaf| leaf.id.to_s }, 0) + @project.update({ leafs: @leaves }) + end + + it 'correctly handles first leaf' do + @leaves[0].attached_below = 'Glued' + @leaves[0].save + @leaf = @leaves[0] + update_attached_to + @project.reload + @leaves = @project.leafs + expect(@leaves[1].attached_above).to eq 'Glued' + end + + it 'correctly handles last leaf' do + @leaves[-1].attached_above = 'Sewn' + @leaves[-1].save + @leaf = @leaves[-1] + update_attached_to + @project.reload + @leaves = @project.leafs + expect(@leaves[-2].attached_below).to eq 'Sewn' + end + + it 'correctly handles middle leaf' do + @leaves[2].update({ attached_above: 'Glued', attached_below: 'Sewn' }) + @leaf = @leaves[2] + update_attached_to + @project.reload + @leaves = @project.leafs + expect(@leaves[1].attached_below).to eq 'Glued' + expect(@leaves[3].attached_above).to eq 'Sewn' + end + end + + describe 'update_conjoined_partner' do + let(:helpers) { ApplicationController.helpers } + + before :each do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leaves = 3.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @group.id.to_s) } + @group.add_members(@leaves.collect { |leaf| leaf.id.to_s }, 0) + @project.update({ leafs: @leaves }) + end + + it 'should reattach 1-2 to 1-3' do + @leaves[0].update({ conjoined_to: @leaves[1].id.to_s }) + @leaves[1].update({ conjoined_to: @leaves[0].id.to_s }) + @leaf = @leaves[0] + update_conjoined_partner(@leaves[2].id.to_s) + @project.reload + @leaves = @project.leafs + expect(@leaves[1].conjoined_to).to be_blank + expect(@leaves[2].conjoined_to).to eq @leaves[0].id.to_s + end + + it 'should reattach 2-3 to 1-3' do + @leaves[1].update({ conjoined_to: @leaves[2].id.to_s }) + @leaves[2].update({ conjoined_to: @leaves[1].id.to_s }) + @leaf = @leaves[0] + update_conjoined_partner(@leaves[2].id.to_s) + @project.reload + @leaves = @project.leafs + expect(@leaves[1].conjoined_to).to be_blank + expect(@leaves[2].conjoined_to).to eq @leaves[0].id.to_s + end + end +end diff --git a/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb new file mode 100644 index 00000000..11bf4135 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb @@ -0,0 +1,131 @@ +require 'rails_helper' + +RSpec.describe ControllerHelper::ProjectsHelper, type: :helper do + describe 'addGroupsLeafsConjoin' do + it 'should create a variety of groups' do + @project = FactoryGirl.create(:project) + addGroupsLeafsConjoin(@project, [ + { 'leaves' => 2 }, + { 'leaves' => 4, 'conjoin' => true }, + { 'leaves' => 3, 'conjoin' => true, 'oddLeaf' => 2 } + ], nil, nil, "Hair") + expect(@project.groups.count).to eq 3 + expect(@project.groups[0].memberIDs.count).to eq 2 + expect(@project.groups[1].memberIDs.count).to eq 4 + expect(Leaf.find(@project.groups[1].memberIDs[0]).conjoined_to).to eq @project.groups[1].memberIDs[3] + expect(Leaf.find(@project.groups[1].memberIDs[1]).conjoined_to).to eq @project.groups[1].memberIDs[2] + expect(Leaf.find(@project.groups[1].memberIDs[2]).conjoined_to).to eq @project.groups[1].memberIDs[1] + expect(Leaf.find(@project.groups[1].memberIDs[3]).conjoined_to).to eq @project.groups[1].memberIDs[0] + expect(@project.groups[2].memberIDs.count).to eq 3 + expect(Leaf.find(@project.groups[2].memberIDs[1]).conjoined_to).to be_blank + expect(Leaf.find(@project.groups[2].memberIDs[0]).conjoined_to).to eq @project.groups[2].memberIDs[2] + expect(Leaf.find(@project.groups[2].memberIDs[2]).conjoined_to).to eq @project.groups[2].memberIDs[0] + end + it 'should generate folio numbers' do + project = FactoryGirl.create(:project) + addGroupsLeafsConjoin(project, [ + { 'leaves' => 2 }, + { 'leaves' => 4, 'conjoin' => true }, + { 'leaves' => 3, 'conjoin' => true, 'oddLeaf' => 2 } + ], 2, nil, "Hair") + expect(Side.find(Leaf.find(project.groups[0].memberIDs[0]).rectoID).folio_number).to eq "2R" + expect(Side.find(Leaf.find(project.groups[0].memberIDs[0]).versoID).folio_number).to eq "2V" + expect(Side.find(Leaf.find(project.groups[0].memberIDs[1]).rectoID).folio_number).to eq "3R" + expect(Side.find(Leaf.find(project.groups[0].memberIDs[1]).versoID).folio_number).to eq "3V" + end + it 'should generate page numbers' do + project = FactoryGirl.create(:project) + addGroupsLeafsConjoin(project, [ + { 'leaves' => 2 }, + { 'leaves' => 4, 'conjoin' => true }, + { 'leaves' => 3, 'conjoin' => true, 'oddLeaf' => 2 } + ], nil, 5, "Hair") + expect(Side.find(Leaf.find(project.groups[0].memberIDs[0]).rectoID).page_number).to eq "5" + expect(Side.find(Leaf.find(project.groups[0].memberIDs[0]).versoID).page_number).to eq "6" + expect(Side.find(Leaf.find(project.groups[0].memberIDs[1]).rectoID).page_number).to eq "7" + expect(Side.find(Leaf.find(project.groups[0].memberIDs[1]).versoID).page_number).to eq "8" + end + it 'should generate correct patterns of texture' do + project = FactoryGirl.create(:project) + addGroupsLeafsConjoin(project, [ + { 'leaves' => 4, 'conjoin' => true}, + { 'leaves' => 4, 'conjoin' => true }, + { 'leaves' => 3, 'conjoin' => true, 'oddLeaf' => 2 } + ], nil, 5, "Flesh") + expect(Side.find(Leaf.find(Group.find(project.groupIDs[0]).memberIDs[0]).rectoID).texture).to eq "Flesh" + expect(Side.find(Leaf.find(Group.find(project.groupIDs[0]).memberIDs[0]).versoID).texture).to eq "Hair" + expect(Side.find(Leaf.find(Group.find(project.groupIDs[0]).memberIDs[1]).rectoID).texture).to eq "Hair" + expect(Side.find(Leaf.find(Group.find(project.groupIDs[0]).memberIDs[1]).versoID).texture).to eq "Flesh" + expect(Side.find(Leaf.find(Group.find(project.groupIDs[0]).memberIDs[2]).rectoID).texture).to eq "Flesh" + expect(Side.find(Leaf.find(Group.find(project.groupIDs[0]).memberIDs[2]).versoID).texture).to eq "Hair" + end + end + + describe 'getManifestInformation' do + before do + stub_request(:get, 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/uoft_hollar.json'), headers: {}) + end + + it 'should pull images' do + result = getManifestInformation('https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest') + expect(result[:name]).to eq "The fables of Aesop / paraphras'd in verse, and adorn'd with sculpture ; by John Ogilby." + expect(result[:images].count).to eq 392 + expect(result[:images][1]).to eq({ label: "Hollar_a_3000_0002", url: "https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002" }) + end + end + + describe 'generateResponse/getLeafMembers' do + before do + stub_request(:get, 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/villanova_boston.json'), headers: {}) + @project = FactoryGirl.create(:project, + title: 'Sample project', + shelfmark: 'Ravenna 384.2339', + metadata: { date: '18th century' }, + preferences: { showTips: true }, + noteTypes: ['Hand', 'Ink', 'Unknown'], + manifests: { '12341234': { id: '12341234', url: 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', name: 'Boston, and Bunker Hill.' } } + ) + # Attach group with 2 leafs - (group with 2 leafs) - 2 conjoined leafs + @testgroup = FactoryGirl.create(:group, project: @project, nestLevel: 1) + @upleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testgroup.id.to_s, nestLevel: 1) } + @testmidgroup = FactoryGirl.create(:group, project: @project, parentID: @testgroup.id.to_s, nestLevel: 2) + @midleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testmidgroup.id.to_s, nestLevel: 2) } + @botleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testgroup.id.to_s, nestLevel: 1) } + @botleafs[1].update(type: 'Endleaf') + @project.add_groupIDs([@testgroup.id.to_s, @testmidgroup.id.to_s], 0) + @testgroup.add_members([@upleafs[0].id.to_s, @upleafs[1].id.to_s, @testmidgroup.id.to_s, @botleafs[0].id.to_s, @botleafs[1].id.to_s], 0) + @testmidgroup.add_members([@midleafs[0].id.to_s, @midleafs[1].id.to_s], 0) + @testnote = FactoryGirl.create(:note, project: @project, title: 'Test Note', type: 'Ink', description: 'This is a test', show: true, objects: {Group: [@testgroup.id.to_s], Leaf: [@botleafs[0].id.to_s], Recto: [@botleafs[0].rectoID], Verso: [@botleafs[0].versoID]}) + end + + it 'returns the right output for the given sample' do + body = generateResponse() + expect(body[:project]).to eq({ + 'id': @project.id.to_s, + 'title': 'Sample project', + 'shelfmark': 'Ravenna 384.2339', + 'metadata': { 'date' => '18th century' }, + 'preferences': { 'showTips' => true }, + 'noteTypes': [ 'Hand', 'Ink', 'Unknown' ], + 'manifests': { '12341234' => { + 'id' => '12341234', + 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', + 'name' => 'Boston, and Bunker Hill.', + 'images' => [ { 'label' => nil, 'url' => 'https://iiif.library.villanova.edu/image/vudl%3A99215', 'manifestID' => '12341234' } ] + } }, + }) + expect(body[:groupIDs]).to eq([@testgroup.id.to_s, @testmidgroup.id.to_s]) + expect(body[:leafIDs]).to eq((@upleafs+@midleafs+@botleafs).collect { |leaf| leaf.id.to_s }) + expect(body[:rectoIDs]).to eq((@upleafs+@midleafs+@botleafs).collect { |leaf| leaf.rectoID }) + expect(body[:versoIDs]).to eq((@upleafs+@midleafs+@botleafs).collect { |leaf| leaf.versoID }) + expect(body[:notes]).to eq({@testnote.id.to_s => { + id: @testnote.id.to_s, + title: 'Test Note', + type: 'Ink', + description: 'This is a test', + show: true, + objects: {'Group' => [@testgroup.id.to_s], 'Leaf' => [@botleafs[0].id.to_s], 'Recto' => [@botleafs[0].rectoID], 'Verso' => [@botleafs[0].versoID]} + }}) + end + end +end diff --git a/viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb b/viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb new file mode 100644 index 00000000..2736eca4 --- /dev/null +++ b/viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb @@ -0,0 +1,186 @@ +require 'rails_helper' + +RSpec.describe ValidationHelper::GroupValidationHelper, type: :helper do + describe "validateAdditionalGroupParams" do + it 'should accept correct parameters' do + result = validateAdditionalGroupParams(1, nil, 1, 4, true, nil) + expect(result).to be_empty + end + + describe "noOfGroups" do + it 'should be required' do + result = validateAdditionalGroupParams(nil, nil, 1, 4, true, nil) + expect(result[:noOfGroups]).to include 'is required' + end + + it 'should be an integer' do + result = validateAdditionalGroupParams('waahoo', nil, 1, 4, true, nil) + expect(result[:noOfGroups]).to include 'should be an Integer' + end + + it 'should range from 1 to 999' do + result = validateAdditionalGroupParams(0, nil, 1, 4, true, nil) + expect(result[:noOfGroups]).to include 'should range from 1 to 999' + result = validateAdditionalGroupParams(1, nil, 1, 4, true, nil) + expect(result).to be_empty + result = validateAdditionalGroupParams(999, nil, 1, 4, true, nil) + expect(result).to be_empty + result = validateAdditionalGroupParams(1000, nil, 1, 4, true, nil) + expect(result[:noOfGroups]).to include 'should range from 1 to 999' + end + end + + describe "parentGroupID" do + before do + @project = FactoryGirl.create(:project) + @parent = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@parent.id.to_s], 0) + end + + it 'should be OK with an existent parent' do + result = validateAdditionalGroupParams(1, @parent.id.to_s, 1, 4, true, nil) + expect(result).to be_empty + end + + it 'should reject a non-existent parent' do + result = validateAdditionalGroupParams(1, @parent.id.to_s+'missing', 1, 4, true, nil) + expect(result[:parentGroupID]).to include "group not found with id #{@parent.id.to_s}missing" + end + end + + describe "memberOrder" do + it 'should not be required if there is no parent' do + result = validateAdditionalGroupParams(1, nil, 1, 4, true, nil) + expect(result).to be_empty + end + + describe 'with parent' do + before do + @project = FactoryGirl.create(:project) + @parent = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@parent.id.to_s], 0) + end + it 'should be required' do + result = validateAdditionalGroupParams(1, @parent.id.to_s, nil, 4, true, nil) + expect(result[:memberOrder]).to include 'is required' + end + it 'should be an Integer' do + result = validateAdditionalGroupParams(1, @parent.id.to_s, 'waahoo', 4, true, nil) + expect(result[:memberOrder]).to include 'should be an Integer' + end + it 'should be greater than 0' do + result = validateAdditionalGroupParams(1, @parent.id.to_s, 0, 4, true, nil) + expect(result[:memberOrder]).to include 'should be greater than 0' + end + end + end + + describe "noOfLeafs" do + it 'should be an integer' do + result = validateAdditionalGroupParams(1, nil, 1, 'waahoo', false, nil) + expect(result[:noOfLeafs]).to include 'should be an Integer' + end + + it 'should range from 1 to 999' do + result = validateAdditionalGroupParams(1, nil, 1, 0, false, nil) + expect(result[:noOfLeafs]).to include 'should range from 1 to 999' + result = validateAdditionalGroupParams(1, nil, 1, 1, false, nil) + expect(result).to be_empty + result = validateAdditionalGroupParams(1, nil, 1, 999, false, nil) + expect(result).to be_empty + result = validateAdditionalGroupParams(1, nil, 1, 1000, false, nil) + expect(result[:noOfLeafs]).to include 'should range from 1 to 999' + end + end + + describe "conjoin" do + it 'should be a Boolean' do + result = validateAdditionalGroupParams(1, nil, 1, 4, 'waahoo', nil) + expect(result[:conjoin]).to include 'should be a Boolean' + end + + it 'should be false if the number of leaves is 1' do + result = validateAdditionalGroupParams(1, nil, 1, 1, true, nil) + expect(result[:conjoin]).to include 'should be false if the number of leaves is 1' + end + end + + describe "oddMemberLeftOut" do + it 'should be an integer' do + result = validateAdditionalGroupParams(1, nil, 1, 3, true, 'waahoo') + expect(result[:oddMemberLeftOut]).to include 'should be an Integer' + end + + it 'should range from 1 to the number of leaves' do + result = validateAdditionalGroupParams(1, nil, 1, 5, true, 0) + expect(result[:oddMemberLeftOut]).to include 'should range from 1 to the number of leaves' + result = validateAdditionalGroupParams(1, nil, 1, 5, true, 1) + expect(result).to be_empty + result = validateAdditionalGroupParams(1, nil, 1, 5, true, 5) + expect(result).to be_empty + result = validateAdditionalGroupParams(1, nil, 1, 5, true, 6) + expect(result[:oddMemberLeftOut]).to include 'should range from 1 to the number of leaves' + end + + it 'should be empty if the number of leaves is even' do + result = validateAdditionalGroupParams(1, nil, 1, 4, true, 2) + expect(result[:oddMemberLeftOut]).to include 'should be empty if the number of leaves is even' + end + end + end + + describe "validateGroupBatchDelete" do + before do + @project = FactoryGirl.create(:project) + @group1 = FactoryGirl.create(:group, project: @project) + @group2 = FactoryGirl.create(:group, project: @project) + @params = [@group1.id.to_s, @group2.id.to_s] + @project.add_groupIDs(@params, 0) + end + + it 'should accept correct parameters' do + result = validateGroupBatchDelete(@params) + expect(result).to be_empty + result = validateGroupBatchDelete([@group2.id.to_s]) + expect(result).to be_empty + end + + it 'should pick out missing groups' do + @params << @group1.id.to_s+'missing' + @params << @group2.id.to_s+'waahoo' + result = validateGroupBatchDelete(@params) + expect(result).to include "group not found with id #{@group1.id.to_s}missing" + expect(result).to include "group not found with id #{@group2.id.to_s}waahoo" + end + end + + describe "validateGroupBatchUpdate" do + before do + @project = FactoryGirl.create(:project) + @group1 = FactoryGirl.create(:quire, project: @project) + @group2 = FactoryGirl.create(:booklet, project: @project) + @params = [ + { id: @group1.id.to_s, attributes: { type: 'Quire' } }, + { id: @group2.id.to_s, attributes: { type: 'Booklet' } } + ] + @project.add_groupIDs([@group1.id.to_s, @group2.id.to_s], 0) + end + + it 'should accept correct parameters' do + result = validateGroupBatchUpdate(@params) + expect(result).to be_empty + end + + it 'should pick out missing groups' do + @params << { id: @group1.id.to_s+'missing', attributes: { type: 'Quire' } } + result = validateGroupBatchUpdate(@params) + expect(result[0][:id]).to include "group not found with id #{@group1.id.to_s}missing" + end + + it 'should pick out bum types' do + @params[0][:attributes][:type] = 'UltraWaahoo' + result = validateGroupBatchUpdate(@params) + expect(result[0][:attributes][:type]).to include 'should be either Quire or Booklet' + end + end +end diff --git a/viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb b/viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb new file mode 100644 index 00000000..8dbced9d --- /dev/null +++ b/viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb @@ -0,0 +1,145 @@ +require 'rails_helper' + +RSpec.describe ValidationHelper::LeafValidationHelper, type: :helper do + before :each do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + end + + let(:project_id) { @project.id.to_s } + let(:group_id) { @group.id.to_s } + + describe 'validateLeafParams' do + it 'should accept correct parameters' do + result = validateLeafParams(project_id, group_id) + expect(result[:project_id]).to be_empty + expect(result[:parentID]).to be_empty + end + + describe 'Project' do + it 'should be required' do + result = validateLeafParams(nil, group_id) + expect(result[:project_id]).to include 'is required' + end + it 'should be a string' do + result = validateLeafParams(3, group_id) + expect(result[:project_id]).to include 'should be a String' + end + it 'should exist' do + result = validateLeafParams(project_id+'missing', group_id) + expect(result[:project_id]).to include 'project not found' + end + end + + describe 'Parent' do + it 'should be required' do + result = validateLeafParams(project_id, nil) + expect(result[:parentID]).to include 'is required' + end + it 'should be a string' do + result = validateLeafParams(project_id, 3) + expect(result[:parentID]).to include 'should be a String' + end + it 'should belong to the same project' do + project2 = FactoryGirl.create(:project) + group2 = FactoryGirl.create(:group, project: project2) + project2.add_groupIDs([group2.id.to_s], 0) + result = validateLeafParams(project_id, group2.id.to_s) + expect(result[:parentID]).to include 'Group with parentID does not have project_id as a member' + end + it 'should exist' do + result = validateLeafParams(project_id, group_id+'missing') + expect(result[:parentID]).to include 'group not found' + end + end + end + + describe 'validateAdditionalLeafParams' do + it 'should accept correct parameters' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 12, true, nil) + expect(result[:memberOrder]).to be_empty + expect(result[:noOfLeafs]).to be_empty + expect(result[:conjoin]).to be_empty + expect(result[:oddMemberLeftOut]).to be_empty + end + + describe 'memberOrder' do + it 'should be required' do + result = validateAdditionalLeafParams(project_id, group_id, nil, 12, true, nil) + expect(result[:memberOrder]).to include 'is required' + end + + it 'should be an integer' do + result = validateAdditionalLeafParams(project_id, group_id, 'waahoo', 12, true, nil) + expect(result[:memberOrder]).to include 'should be an Integer' + end + + it 'should be positive' do + result = validateAdditionalLeafParams(project_id, group_id, 0, 12, true, nil) + expect(result[:memberOrder]).to include 'should be greater than 0' + end + end + + describe 'noOfLeafs' do + it 'should be required' do + result = validateAdditionalLeafParams(project_id, group_id, 1, nil, true, nil) + expect(result[:noOfLeafs]).to include 'is required' + end + + it 'should be an integer' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 'waahoo', true, nil) + expect(result[:noOfLeafs]).to include 'should be an Integer' + end + + it 'should be positive' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 0, true, nil) + expect(result[:noOfLeafs]).to include 'should range from 1 to 999' + result = validateAdditionalLeafParams(project_id, group_id, 1, 1, true, nil) + expect(result[:noOfLeafs]).to be_empty + end + + it 'should be at most 3 digits' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 999, true, nil) + expect(result[:noOfLeafs]).to be_empty + result = validateAdditionalLeafParams(project_id, group_id, 1, 1000, true, nil) + expect(result[:noOfLeafs]).to include 'should range from 1 to 999' + end + end + + describe 'conjoin' do + it 'should be a Boolean if present' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 12, 'waahoo', nil) + expect(result[:conjoin]).to include 'should be a Boolean' + end + + it 'should be false if noOfLeafs is 1' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 1, true, nil) + expect(result[:conjoin]).to include 'should be false if the number of leaves is 1' + end + end + + describe 'oddMemberLeftOut' do + it 'should be an integer if present' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 11, true, 'waahoo') + expect(result[:oddMemberLeftOut]).to include 'should be an Integer' + end + + it 'should be strictly between 0 and noOfLeafs' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 11, true, 0) + expect(result[:oddMemberLeftOut]).to include 'should range from 1 to the number of leaves' + result = validateAdditionalLeafParams(project_id, group_id, 1, 11, true, 1) + expect(result[:oddMemberLeftOut]).to be_empty + result = validateAdditionalLeafParams(project_id, group_id, 1, 11, true, 11) + expect(result[:oddMemberLeftOut]).to be_empty + result = validateAdditionalLeafParams(project_id, group_id, 1, 11, true, 12) + expect(result[:oddMemberLeftOut]).to include 'should range from 1 to the number of leaves' + end + + it 'should be empty if noOfLeafs is even' do + result = validateAdditionalLeafParams(project_id, group_id, 1, 12, true, 2) + expect(result[:oddMemberLeftOut]).to include 'should be present only if the number of leaves is odd' + end + end + end +end diff --git a/viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb b/viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb new file mode 100644 index 00000000..7a326e7b --- /dev/null +++ b/viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb @@ -0,0 +1,72 @@ +require 'rails_helper' + +RSpec.describe ValidationHelper::ProjectValidationHelper, type: :helper do + describe "validateProjectCreateGroupsParams" do + before :each do + @params = [ + { 'leaves' => 3, 'oddLeaf' => 1, 'conjoin' => false }, + { 'leaves' => 6, 'oddLeaf' => 0, 'conjoin' => true } + ] + end + + it 'should allow nil' do + result = validateProjectCreateGroupsParams(nil) + expect(result[:errors]).to be_empty + expect(result[:status]).to be true + end + + it 'should allow the standard params' do + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors]).to be_empty + expect(result[:status]).to be true + end + + describe "Leaf count" do + it 'should be integers only' do + @params[0]['leaves'] = 'waahoo' + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors][0][:leaves]).to include 'should be an Integer' + expect(result[:status]).to be false + end + + it 'should be positive' do + @params[1]['leaves'] = 0 + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors][0][:leaves]).to include 'should be greater than 0' + expect(result[:status]).to be false + end + end + + describe "Odd leaf parity" do + it 'should be integers only' do + @params[0]['oddLeaf'] = 'waahoo' + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors][0][:oddLeaf]).to include 'should be an Integer' + expect(result[:status]).to be false + end + + it 'should be positive' do + @params[0]['oddLeaf'] = 0 + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors][0][:oddLeaf]).to include 'should be greater than 0' + expect(result[:status]).to be false + end + + it 'should not be greater than leaves' do + @params[0]['oddLeaf'] = 7 + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors][0][:oddLeaf]).to include 'cannot be greater than leaves' + expect(result[:status]).to be false + end + end + + describe "Conjoin" do + it 'should be Boolean' do + @params[1]['conjoin'] = 'waahoo' + result = validateProjectCreateGroupsParams(@params) + expect(result[:errors][0][:conjoin]).to include 'should be a Boolean' + expect(result[:status]).to be false + end + end + end +end diff --git a/viscoll-api/spec/mailers/feedback_spec.rb b/viscoll-api/spec/mailers/feedback_spec.rb new file mode 100644 index 00000000..b386360a --- /dev/null +++ b/viscoll-api/spec/mailers/feedback_spec.rb @@ -0,0 +1,22 @@ +require "rails_helper" + +RSpec.describe FeedbackMailer, type: :mailer do + context 'user submits a feedback' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + end + + let(:mail) { FeedbackMailer.sendFeedback("Title of feedback", "My message", nil, nil, @user.id)} + + it "should send email" do + expect(mail.subject).to eq("Title of feedback") + expect(mail.to).to eq(["utlviscoll@library.utoronto.ca"]) + end + + it "should render body" do + expect(mail.body.raw_source).to include("My message") + expect(mail.body.raw_source).to include(@user.name) + expect(mail.body.raw_source).to include(@user.email) + end + end +end diff --git a/viscoll-api/spec/mailers/previews/feedback_preview.rb b/viscoll-api/spec/mailers/previews/feedback_preview.rb new file mode 100644 index 00000000..41dfdef5 --- /dev/null +++ b/viscoll-api/spec/mailers/previews/feedback_preview.rb @@ -0,0 +1,4 @@ +# Preview all emails at http://localhost:3000/rails/mailers/feedback +class FeedbackPreview < ActionMailer::Preview + +end diff --git a/viscoll-api/spec/models/group_spec.rb b/viscoll-api/spec/models/group_spec.rb new file mode 100644 index 00000000..8ce7f7b5 --- /dev/null +++ b/viscoll-api/spec/models/group_spec.rb @@ -0,0 +1,106 @@ +require 'rails_helper' + +RSpec.describe Group, type: :model do + it { is_expected.to be_mongoid_document } + + it { is_expected.to have_field(:title).of_type(String) } + it { is_expected.to have_field(:type).of_type(String) } + it { is_expected.to have_field(:tacketed).of_type(Array) } + it { is_expected.to have_field(:sewing).of_type(Array) } + it { is_expected.to have_field(:nestLevel).of_type(Integer) } + it { is_expected.to have_field(:parentID).of_type(String) } + it { is_expected.to have_field(:memberIDs).of_type(Array) } + + it { is_expected.to belong_to(:project) } + it { is_expected.to have_and_belong_to_many(:notes) } + + before(:each) do + @project = FactoryGirl.create(:project) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id], 0) + end + + describe "Initialization" do + it "should prefix its ID" do + expect(@group.id.to_s[0..5]).to eq "Group_" + end + end + + describe "Member handling" do + it "should add member IDs" do + @group.add_members(['abcd', 'efgh'], 0) + expect(@group.memberIDs).to eq ['abcd', 'efgh'] + end + + it "should add additional member IDs" do + @group.add_members(['abcd', 'efgh', 'ijkl'], 0) + expect(@group.memberIDs).to eq ['abcd', 'efgh', 'ijkl'] + @group.add_members(['1234', '5678'], 3) + expect(@group.memberIDs).to eq ['abcd', 'efgh', '1234', '5678', 'ijkl'] + end + + it "should respect the save flag" do + @group.add_members(['abcd', 'efgh', 'ijkl'], 0) + expect(@group.memberIDs).to eq ['abcd', 'efgh', 'ijkl'] + @group.add_members(['1234', '5678'], 3, false) + expect(@group.memberIDs).to eq ['abcd', 'efgh', '1234', '5678', 'ijkl'] + @group.reload + expect(@group.memberIDs).to eq ['abcd', 'efgh', 'ijkl'] + end + + it "should remove member IDs" do + @group.add_members(['abcd', 'efgh', 'ijkl'], 0) + expect(@group.memberIDs).to eq ['abcd', 'efgh', 'ijkl'] + @group.remove_members(['abcd', 'ijkl']) + expect(@group.memberIDs).to eq ['efgh'] + end + end + + describe "On-destroy hooks" do + it "should remove itself from an associated note" do + note = FactoryGirl.create(:note, project: @project, objects: {Group: [@group.id], Leaf: [], Recto: [], Verso: []}) + @group.notes << note + @group.save + @group.destroy + expect(note.objects[:Group]).to be_empty + end + + it "should remove itself from an associated project" do + @group.destroy + expect(@project.groupIDs).to be_empty + end + + it "should remove itself from a parent group" do + parent_group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([parent_group.id.to_s], 0) + @group.parentID = parent_group.id + parent_group.add_members([@group.id.to_s], 0) + @group.save + expect(parent_group.memberIDs).not_to be_empty + @group.destroy + parent_group.reload + expect(parent_group.memberIDs).to be_empty + end + + it "should remove its members" do + subgroup = FactoryGirl.create(:group, project: @project) + subgroup_id = subgroup.id + @project.add_groupIDs([subgroup.id.to_s], 0) + subgroup.parentID = @group.id + @group.add_members([subgroup.id.to_s], 0) + subgroup.save + expect(@group.memberIDs).to include(subgroup.id.to_s) + + subleaf = FactoryGirl.create(:leaf, project: @project) + subleaf_id = subleaf.id + @group.add_members([subleaf.id.to_s], 0) + subleaf.parentID = @group.id.to_s + subleaf.save + expect(@group.memberIDs).to include(subleaf.id.to_s) + + @group.destroy + expect(Group.where(id: subgroup_id).exists?).to be false + expect(Leaf.where(id: subleaf_id).exists?).to be false + end + end +end diff --git a/viscoll-api/spec/models/grouping_spec.rb b/viscoll-api/spec/models/grouping_spec.rb new file mode 100644 index 00000000..b5e13381 --- /dev/null +++ b/viscoll-api/spec/models/grouping_spec.rb @@ -0,0 +1,21 @@ +# require 'rails_helper' + +# RSpec.describe Grouping, type: :model do +# it { is_expected.to be_mongoid_document } +# it { is_expected.to have_field(:order).of_type(Integer) } +# it { is_expected.to belong_to(:group).with_foreign_key(:group_id) } +# it { is_expected.to belong_to(:member).with_foreign_key(:member_id) } + +# before(:each) do +# @project = FactoryGirl.create(:project) +# @leaf = FactoryGirl.create(:leaf, project: @project) +# @group = FactoryGirl.create(:quire) +# end + +# it "can delete a member" do +# @group.add_member(@leaf, 1) +# @leaf.destroy +# expect(@group.get_members.size).to eq 0 +# end + +# end \ No newline at end of file diff --git a/viscoll-api/spec/models/image_spec.rb b/viscoll-api/spec/models/image_spec.rb new file mode 100644 index 00000000..5eb71eba --- /dev/null +++ b/viscoll-api/spec/models/image_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +RSpec.describe Image, type: :model do + it { is_expected.to be_mongoid_document } + + it { is_expected.to have_field(:filename).of_type(String) } + it { is_expected.to have_field(:projectIDs).of_type(Array) } + it { is_expected.to have_field(:sideIDs).of_type(Array) } + + it { is_expected.to belong_to(:user) } + + before(:each) do + @user = FactoryGirl.create(:user) + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1, 2]]) + @image = FactoryGirl.create(:image, user: @user) + end + + describe 'Validations' do + it 'should be valid to start with' do + expect(@image).to be_valid + end + + it 'should not be valid with a duplicate file name' do + duplicate_image = FactoryGirl.build(:image, user: @user, filename: @image.filename) + expect(duplicate_image).not_to be_valid + end + end + + describe 'Side unlinking hook' do + before do + @side = @project.sides[1] + @side.update(image: { + manifestID: 'DIYImages', + label: 'hello.png', + url: 'http://127.0.0.1:3001/pixel.png' + }) + @image.update(sideIDs: [@side.id.to_s]) + end + + it 'should unhook the side upon deletion' do + @image.destroy + @side.reload + expect(@side.image).to be_blank + end + end +end diff --git a/viscoll-api/spec/models/leaf_spec.rb b/viscoll-api/spec/models/leaf_spec.rb new file mode 100644 index 00000000..8f9ac8ce --- /dev/null +++ b/viscoll-api/spec/models/leaf_spec.rb @@ -0,0 +1,68 @@ +require 'rails_helper' + +RSpec.describe Leaf, type: :model do + it { is_expected.to be_mongoid_document } + + it { is_expected.to have_field(:material).of_type(String) } + it { is_expected.to have_field(:type).of_type(String) } + it { is_expected.to have_field(:conjoined_to).of_type(String) } + it { is_expected.to have_field(:attached_above).of_type(String) } + it { is_expected.to have_field(:attached_below).of_type(String) } + it { is_expected.to have_field(:stubType).of_type(String) } + it { is_expected.to have_field(:parentID).of_type(String) } + it { is_expected.to have_field(:nestLevel).of_type(Integer) } + it { is_expected.to have_field(:rectoID).of_type(String) } + it { is_expected.to have_field(:versoID).of_type(String) } + + it { is_expected.to belong_to(:project) } + it { is_expected.to have_and_belong_to_many(:notes) } + + before(:each) do + @project = FactoryGirl.create(:project) + @leaf = FactoryGirl.create(:leaf, project: @project) + @group = FactoryGirl.create(:group, project: @project) + @group.add_members([@leaf.id.to_s], 0) + @leaf.parentID = @group.id + @leaf.save + end + + describe "Initialization" do + it "should have a prefixed ID" do + expect(@leaf.id.to_s[0..4]).to eq "Leaf_" + end + + it "should add two sides" do + expect(Side.where(id: @leaf.rectoID).exists?).to be true + expect(Side.where(id: @leaf.versoID).exists?).to be true + end + end + + it "should be able to unlink itself from a group" do + @group = FactoryGirl.create(:group, project: @project) + @group.add_members([@leaf.id.to_s], 0) + @leaf.parentID = @group.id + @leaf.save + expect(@group.memberIDs).to include(@leaf.id.to_s) + @leaf.remove_from_group + @group.reload + expect(@group.memberIDs).not_to include(@leaf.id.to_s) + end + + describe "Destruction" do + it "should unlink its notes" do + subnote = FactoryGirl.create(:note, project: @project, objects: {Group: [], Leaf: [@leaf.id], Recto: [], Verso: []}) + @leaf.notes << subnote + @leaf.save + @leaf.destroy + expect(subnote.objects[:Leaf]).to be_empty + end + + it "should destroy its sides" do + rectoId = @leaf.rectoID + versoId = @leaf.versoID + @leaf.destroy + expect(Side.where(id: rectoId).exists?).to be false + expect(Side.where(id: versoId).exists?).to be false + end + end +end diff --git a/viscoll-api/spec/models/note_spec.rb b/viscoll-api/spec/models/note_spec.rb new file mode 100644 index 00000000..2e502605 --- /dev/null +++ b/viscoll-api/spec/models/note_spec.rb @@ -0,0 +1,77 @@ +require 'rails_helper' + +RSpec.describe Note, type: :model do + it { is_expected.to be_mongoid_document } + + it { is_expected.to have_field(:title).of_type(String) } + it { is_expected.to have_field(:type).of_type(String) } + it { is_expected.to have_field(:description).of_type(String) } + it { is_expected.to have_field(:objects).of_type(Hash) } + it { is_expected.to have_field(:show).of_type(Mongoid::Boolean) } + + it { is_expected.to belong_to(:project) } + + before :each do + @project = FactoryGirl.create(:project, noteTypes: ['Ink']) + @group = FactoryGirl.create(:group, project: @project) + @leaf = FactoryGirl.create(:leaf, project: @project, parentID: @group.id.to_s) + @side1 = Side.find(id: @leaf.rectoID) + @side2 = Side.find(id: @leaf.versoID) + @project.add_groupIDs([@group.id.to_s], 0) + @group.add_members([@leaf.id.to_s], 0) + @note = FactoryGirl.create(:note, project: @project, type: ['Ink'], objects: {Group: [@group.id.to_s], Leaf: [@leaf.id.to_s], Recto: [@side1.id.to_s], Verso: [@side2.id.to_s]} ) + @group.notes << @note + @group.save + @leaf.notes << @note + @leaf.save + @side1.notes << @note + @side1.save + @side2.notes << @note + @side2.save + end + + describe "Validations" do + it "should require a title" do + @note.title = '' + expect(@note).not_to be_valid + end + it "should be unique within the project" do + duplicate_note = FactoryGirl.create(:note, project: @project, type: ['Ink'], objects: {Group: [@group.id.to_s], Leaf: [], Recto: [], Verso: []} ) + duplicate_note.title = @note.title + expect(duplicate_note).not_to be_valid + end + it "should not need to be unique globally" do + project2 = FactoryGirl.create(:project) + group2 = FactoryGirl.create(:group, project: project2) + project2.add_groupIDs([group2.id.to_s], 0) + note2 = FactoryGirl.create(:note, project: project2, type: ['Ink'], objects: {Group: [group2.id.to_s], Leaf: [], Recto: [], Verso: []}) + expect(note2).to be_valid + end + it "should require a type" do + @note.type = '' + expect(@note).not_to be_valid + end + end + + describe "Destroy hooks" do + before do + @note.destroy + end + it "updates linked group" do + @group.reload + expect(@group.notes).to be_empty + end + it "updates linked leaf" do + @leaf.reload + expect(@leaf.notes).to be_empty + end + it "updates linked recto side" do + @side1.reload + expect(@side1.notes).to be_empty + end + it "updates linked verso side" do + @side2.reload + expect(@side2.notes).to be_empty + end + end +end diff --git a/viscoll-api/spec/models/project_spec.rb b/viscoll-api/spec/models/project_spec.rb new file mode 100644 index 00000000..e0d3b2d1 --- /dev/null +++ b/viscoll-api/spec/models/project_spec.rb @@ -0,0 +1,78 @@ +require 'rails_helper' + +RSpec.describe Project, type: :model do + it { is_expected.to be_mongoid_document } + + it { is_expected.to have_field(:title).of_type(String) } + it { is_expected.to have_field(:shelfmark).of_type(String) } + it { is_expected.to have_field(:metadata).of_type(Hash) } + it { is_expected.to have_field(:manifests).of_type(Hash) } + it { is_expected.to have_field(:noteTypes).of_type(Array) } + it { is_expected.to have_field(:preferences).of_type(Hash) } + it { is_expected.to have_field(:groupIDs).of_type(Array) } + + it { is_expected.to belong_to(:user) } + it { is_expected.to have_many(:groups) } + it { is_expected.to have_many(:leafs) } + it { is_expected.to have_many(:sides) } + it { is_expected.to have_many(:notes) } + + before(:each) do + @user = FactoryGirl.create(:user) + @project = FactoryGirl.create(:project, user: @user) + end + + describe "Validations" do + it "should require a title" do + @project.title = '' + expect(@project).not_to be_valid + end + + it "should be unique to the same user" do + @duplicated_project = FactoryGirl.create(:project, user: @user) + @project.title = @duplicated_project.title + expect(@project).not_to be_valid + end + + it "can be duplicated for different users" do + @user2 = FactoryGirl.create(:user) + @duplicated_project = FactoryGirl.create(:project, user: @user2) + @project.title = @duplicated_project.title + expect(@project).to be_valid + end + end + + describe "Group IDs" do + it "should add group IDs properly" do + @project.add_groupIDs(['abcd', 'efgh'], 0) + expect(@project.groupIDs).to eq ['abcd', 'efgh'] + end + + it "should insert group IDs properly" do + @project.add_groupIDs(['abcd', 'efgh', 'ijkl'], 0) + @project.add_groupIDs(['1234', '5678'], 1) + expect(@project.groupIDs).to eq ['abcd', '1234', '5678', 'efgh', 'ijkl'] + end + + it "should remove group IDs properly" do + @project.add_groupIDs(['abcd', 'efgh', 'ijkl'], 0) + @project.remove_groupID('efgh') + expect(@project.groupIDs).to eq ['abcd', 'ijkl'] + end + end + + describe "Image unlinking hook" do + before do + @project1 = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1,2]]) + @project2 = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1,2]]) + @image = FactoryGirl.create(:pixel, user: @user, projectIDs: [@project1.id.to_s, @project2.id.to_s], sideIDs: [@project1.sides[0].id.to_s, @project2.sides[0].id.to_s]) + end + + it 'should unhook from deleted project and sides' do + @project2.destroy! + @image.reload + expect(@image.projectIDs).to eq [@project1.id.to_s] + expect(@image.sideIDs).to eq [@project1.sides[0].id.to_s] + end + end +end diff --git a/viscoll-api/spec/models/side_spec.rb b/viscoll-api/spec/models/side_spec.rb new file mode 100644 index 00000000..71e92ad3 --- /dev/null +++ b/viscoll-api/spec/models/side_spec.rb @@ -0,0 +1,43 @@ +require 'rails_helper' + +RSpec.describe Side, type: :model do + it { is_expected.to be_mongoid_document } + + it { is_expected.to have_field(:folio_number).of_type(String) } + it { is_expected.to have_field(:texture).of_type(String) } + it { is_expected.to have_field(:script_direction).of_type(String) } + it { is_expected.to have_field(:image).of_type(Hash) } + it { is_expected.to have_field(:parentID).of_type(String) } + + it { is_expected.to belong_to(:project) } + it { is_expected.to have_and_belong_to_many(:notes) } + + before :each do + @user = FactoryGirl.create(:user) + @project = FactoryGirl.create(:project, user: @user) + @leaf = FactoryGirl.create(:leaf, project: @project) + @side = Side.find(id: @leaf.rectoID) + end + + describe "Destruction hooks" do + it "should unlink attached notes" do + note = FactoryGirl.create(:note, project: @project, objects: {Group: [], Leaf: [], Recto: [@side.id.to_s], Verso: []} ) + note2 = FactoryGirl.create(:note, project: @project, objects: {Group: [], Leaf: [], Recto: [], Verso: [@side.id.to_s]} ) + @side.notes << [note, note2] + @side.save + expect(@side.notes).to include note + expect(@side.notes).to include note2 + @side.destroy + expect(note.objects[:Recto]).to be_empty + expect(note2.objects[:Verso]).to be_empty + end + + it "should unlink attached image" do + image = FactoryGirl.create(:pixel, user: @user, filename: 'pixel.png', projectIDs: [@project.id.to_s], sideIDs: [@side.id.to_s]) + @side.update(image: { url: "http://127.0.0.1:12345/images/#{image.id}_pixel.png", label: 'Pixel', manifestID: 'DIYImages' }) + @side.destroy + image.reload + expect(image.sideIDs).to be_empty + end + end +end diff --git a/spec/rails_helper.rb b/viscoll-api/spec/rails_helper.rb similarity index 68% rename from spec/rails_helper.rb rename to viscoll-api/spec/rails_helper.rb index 73adb1cb..9de7ea4c 100644 --- a/spec/rails_helper.rb +++ b/viscoll-api/spec/rails_helper.rb @@ -1,11 +1,21 @@ +# require database cleaner at the top level +require 'database_cleaner' + # This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' + ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) + # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? -require 'spec_helper' + require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! +require 'mongoid-rspec' +require 'rails_jwt_auth/spec/helpers' + +# Rails.application.eager_load! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are @@ -22,6 +32,14 @@ # # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } +# configure shoulda matchers to use rspec as the test framework and full matcher libraries for rails +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end + RSpec.configure do |config| # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and @@ -42,4 +60,25 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + + # add `FactoryGirl` methods + config.include FactoryGirl::Syntax::Methods + + # add 'Mongoid' matchers + config.include Mongoid::Matchers, type: :model + + # add 'WardenHelper' + config.include RailsJwtAuth::Spec::Helpers, :type => :request + + # start by truncating all the tables but then use the faster transaction strategy the rest of the time. + config.before(:suite) do + DatabaseCleaner.clean_with(:truncation) + end + + # start the transaction strategy as examples are run + config.around(:each) do |example| + DatabaseCleaner.cleaning do + example.run + end + end end diff --git a/viscoll-api/spec/requests/authentication/delete_session_spec.rb b/viscoll-api/spec/requests/authentication/delete_session_spec.rb new file mode 100644 index 00000000..e6a21893 --- /dev/null +++ b/viscoll-api/spec/requests/authentication/delete_session_spec.rb @@ -0,0 +1,73 @@ +require 'rails_helper' + +describe "DELETE /session", :type => :request do + context 'without token in header' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + @user.confirmation_token = nil + @user.confirmed_at = "2017-07-12T16:08:25.278Z" + @user.save + delete '/session' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end + + context 'with token in header' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + @user.confirmation_token = nil + @user.confirmed_at = "2017-07-12T16:08:25.278Z" + @user.save + end + + context 'and token is invalid' do + before do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + authToken = JSON.parse(response.body)['session']['jwt']+"someInvalidStuff" + delete '/session', params: '', headers: {'Authorization' => authToken} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Header: Signature verification raised') + end + end + + context 'and token format is wrong' do + before do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + delete '/session', params: '', headers: {'Authorization' => "invalidTokenFormat"} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Header: Not enough or too many segments') + end + end + + context 'and token is valid' do + before do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + authToken = JSON.parse(response.body)['session']['jwt'] + delete '/session', params: '', headers: {'Authorization' => authToken} + end + + it 'returns 204 no content response' do + expect(response).to have_http_status(:no_content) + end + + it 'clears the auth tokens of the user' do + expect(User.find(@user.id).auth_tokens).to be_empty + end + end + end +end diff --git a/viscoll-api/spec/requests/authentication/post_password_spec.rb b/viscoll-api/spec/requests/authentication/post_password_spec.rb new file mode 100644 index 00000000..389a5902 --- /dev/null +++ b/viscoll-api/spec/requests/authentication/post_password_spec.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +describe "POST /password", :type => :request do + context 'with valid params' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + @user.confirmation_token = nil + @user.confirmed_at = "2017-07-12T16:08:25.278Z" + @user.save + post '/password', params: {:password => {:email => "user@mail.com"}} + end + + it 'returns a successful no_content response' do + expect(response).to have_http_status(:no_content) + end + + it 'creates fields for reset_password in user record' do + expect(User.find(@user.id).reset_password_token).not_to eq(nil) + expect(User.find(@user.id).reset_password_sent_at).not_to eq(nil) + end + end + + context 'with invalid params' do + context 'and unconfirmed user' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + post '/password', params: {:password => {:email => "user@mail.com"}} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['errors']['email']).to eq(['unconfirmed email']) + end + + it 'doest not create fields for reset_password in user record' do + expect(User.find(@user.id).reset_password_token).to eq(nil) + expect(User.find(@user.id).reset_password_sent_at).to eq(nil) + end + end + + context 'and no valid user' do + before do + post '/password', params: {:password => {:email => "user@mail.com"}} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['errors']['email']).to eq(['not found']) + end + end + end +end diff --git a/viscoll-api/spec/requests/authentication/post_registration_spec.rb b/viscoll-api/spec/requests/authentication/post_registration_spec.rb new file mode 100644 index 00000000..324c393c --- /dev/null +++ b/viscoll-api/spec/requests/authentication/post_registration_spec.rb @@ -0,0 +1,125 @@ +require 'rails_helper' + +describe "POST /registration", :type => :request do + context 'with valid params' do + before do + post '/registration', params: {:user => { :email=> "user@mail.com", :password => "user", :name=>"user" }} + end + + it 'returns with a successful 200 response' do + expect(response).to have_http_status(:created) + end + + it 'returns an user object in the response body' do + expect(JSON.parse(response.body)['user']).not_to be_empty + expect(JSON.parse(response.body)['user']['email']).to eq('user@mail.com') + expect(JSON.parse(response.body)['user']['name']).to eq('user') + end + + it 'returns an email confirmation token with the response body' do + expect(JSON.parse(response.body)['user']['confirmation_token']).not_to be_empty + expect(JSON.parse(response.body)['user']['confirmation_sent_at']).not_to be_empty + end + + it 'creates an User object in the database' do + expect(User.count).to eq(1) + end + end + + context 'with invalid params' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + @user.confirmation_token = nil + @user.confirmed_at = "2017-07-12T16:08:25.278Z" + @user.save + end + + context 'where email is empty' do + before do + post '/registration', params: {:user => { :email=> "", :password => "newUser", :name=>"newUser" }} + end + + it 'returns an appropriate error message with 422 code' do + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['errors']['email']).to eq(['can\'t be blank', 'is not an email']) + end + + it 'does not create another User object in the database' do + expect(User.count).to eq(1) + end + end + + context 'where email is invalid' do + before do + post '/registration', params: {:user => { :email=> "ghost", :password => "newUser", :name=>"newUser" }} + end + + it 'returns an appropriate error message with 422 code' do + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['errors']['email']).to eq(['is not an email']) + end + + it 'does not create another User object in the database' do + expect(User.count).to eq(1) + end + end + + context 'where email is already taken' do + before do + post '/registration', params: {:user => { :email=> "user@mail.com", :password => "user", :name=>"user" }} + end + + it 'returns an appropriate error message with 422 code' do + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['errors']['email']).to eq(['is already taken']) + end + + it 'does not create another User object in the database' do + expect(User.count).to eq(1) + end + end + + context 'where password is empty' do + before do + post '/registration', params: {:user => { :email=> "newUser@mail.com", :password => "", :name=>"newUser" }} + end + + it 'returns an appropriate error message with 422 code' do + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['errors']['password']).to eq(['can\'t be blank']) + end + + it 'does not create another User object in the database' do + expect(User.count).to eq(1) + end + end + + context 'where email and password are invalid' do + before do + post '/registration', params: {:user => { :email=> "ghost", :password => "", :name=>"newUser" }} + end + + it 'returns an appropriate error message with 422 code' do + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['errors']['email']).to eq(['is not an email']) + expect(JSON.parse(response.body)['errors']['password']).to eq(['can\'t be blank']) + end + + it 'does not create another User object in the database' do + expect(User.count).to eq(1) + end + end + + context 'where an exception is thrown' do + before do + allow_any_instance_of(RailsJwtAuth.model).to receive(:save).and_raise('Exception') + post '/registration', params: {:user => { :email=> "user@mail.com", :password => "user", :name=>"user" }} + end + + it 'returns an appropriate error message with 422 code' do + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['error']).to eq 'Exception' + end + end + end +end diff --git a/viscoll-api/spec/requests/authentication/post_session_spec.rb b/viscoll-api/spec/requests/authentication/post_session_spec.rb new file mode 100644 index 00000000..5c5732d9 --- /dev/null +++ b/viscoll-api/spec/requests/authentication/post_session_spec.rb @@ -0,0 +1,84 @@ +require 'rails_helper' + +describe "POST /session", :type => :request do + context 'when the user does not exist' do + before do + post '/session', params: {:session => { :email=> "ghost@mail.com", :password => "ghost" }} + end + + it 'returns an invalid email / password error message' do + expect(JSON.parse(response.body)['errors']['session'][0]).to eq('invalid email / password') + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'when the user exist' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + end + + context 'and user email is not confirmed' do + it 'returns unconfirmed email error' do + expect(JSON.parse(response.body)['errors']['session'][0]).to eq('unconfirmed email') + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and user email is confirmed' do + before do + @user.confirmation_token = nil + @user.confirmed_at = "2017-07-12T16:08:25.278Z" + @user.save + @project1 = Project.create(:title => "first project", :user_id => @user.id) + @project2 = Project.create(:title => "second project", :user_id => @user.id) + @project3 = Project.create(:title => "some other user project", :user_id => "") + end + + context 'and request with invalid params' do + before do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "wrong" }} + end + + it 'returns an invalid email / password error message' do + expect(JSON.parse(response.body)['errors']['session'][0]).to eq('invalid email / password') + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and request with valid params' do + before do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + end + + it 'returns the user session token' do + expect(JSON.parse(response.body)['session']['jwt']).not_to be_empty + expect(JSON.parse(response.body)['session']['email']).to eq("user@mail.com") + end + + it 'creates the auth_tokens for the user' do + expect(User.find(@user.id).auth_tokens).not_to be_empty + end + + it 'returns all the projects of this user' do + expect(JSON.parse(response.body)['session']['projects'].size).to eq(2) + expect(JSON.parse(response.body)['session']['projects'][0]["title"]).to eq("first project") + expect(JSON.parse(response.body)['session']['projects'][1]["title"]).to eq("second project") + end + + it 'returns an ok status' do + expect(response).to have_http_status(:ok) + end + end + end + end +end diff --git a/viscoll-api/spec/requests/authentication/put_confirmation_spec.rb b/viscoll-api/spec/requests/authentication/put_confirmation_spec.rb new file mode 100644 index 00000000..01f5a231 --- /dev/null +++ b/viscoll-api/spec/requests/authentication/put_confirmation_spec.rb @@ -0,0 +1,32 @@ +require 'rails_helper' + +describe "PUT /confirmation", :type => :request do + context 'with invalid token' do + before do + put '/confirmation', params: {:confirmation_token => "invalidToken"} + end + + it 'returns an invalid token message' do + expect(JSON.parse(response.body)['errors']['confirmation_token']).to eq(['not found']) + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'with valid token' do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + end + + it 'returns successful response code' do + expect(response).to have_http_status(:no_content) + end + + it 'clears the confirmation token in user record' do + expect(User.find(@user.id).confirmation_token).to eq(nil) + end + end +end diff --git a/viscoll-api/spec/requests/authentication/put_password_spec.rb b/viscoll-api/spec/requests/authentication/put_password_spec.rb new file mode 100644 index 00000000..fd0b6d3f --- /dev/null +++ b/viscoll-api/spec/requests/authentication/put_password_spec.rb @@ -0,0 +1,100 @@ +require 'rails_helper' + +describe "PUT /password", :type => :request do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + @user.confirmation_token = nil + @user.confirmed_at = "2017-07-12T16:08:25.278Z" + @user.save + post '/password', params: {:password => {:email => "user@mail.com"}} + @user = User.find(@user.id) + end + + context 'with valid params' do + before do + put '/password', params: {:reset_password_token => @user.reset_password_token, :password => {:password => "newUser", :password_confirmation => "newUser"}} + end + + it 'returns a successful no_content response' do + expect(response).to have_http_status(:no_content) + end + + it 'clears the field for reset_password_token in user record' do + expect(User.find(@user.id).reset_password_token).to eq(nil) + end + + it 'updates the user password in the database' do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "newUser" }} + expect(JSON.parse(response.body)['session']['jwt']).not_to be_empty + expect(JSON.parse(response.body)['session']['email']).to eq("user@mail.com") + end + end + + context 'with invalid params' do + context 'and reset token expired' do + before do + @user.reset_password_sent_at = "2017-07-12T16:08:30.278Z" + @user.save + put '/password', params: {:reset_password_token => @user.reset_password_token, :password => {:password => "newUser", :password_confirmation => "newUser"}} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['errors']['reset_password_token']).to eq(['has expired, please request a new one']) + end + + it 'does not not clear field for reset_password_token in user record' do + expect(User.find(@user.id).reset_password_token).not_to eq(nil) + end + end + + context 'and invalid reset token' do + before do + put '/password', params: {:reset_password_token => "invalidToken", :password => {:password => "newUser", :password_confirmation => "newUser"}} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['errors']['reset_password_token']).to eq(['not found']) + end + + it 'does not not clear field for reset_password_token in user record' do + expect(User.find(@user.id).reset_password_token).not_to eq(nil) + end + end + + context 'and blank password' do + before do + put '/password', params: {:reset_password_token => @user.reset_password_token, :password => {:password => "", :password_confirmation => "newUser"}} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['errors']['password']).to eq(['blank']) + end + end + + context 'and no matching passwords' do + before do + put '/password', params: {:reset_password_token => @user.reset_password_token, :password => {:password => "newUser", :password_confirmation => "newUserGhost"}} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['errors']['password_confirmation']).to eq(['doesn\'t match Password']) + end + end + end +end diff --git a/viscoll-api/spec/requests/feedback/create_feedback_spec.rb b/viscoll-api/spec/requests/feedback/create_feedback_spec.rb new file mode 100644 index 00000000..927435ac --- /dev/null +++ b/viscoll-api/spec/requests/feedback/create_feedback_spec.rb @@ -0,0 +1,103 @@ +require 'rails_helper' + +describe "POST /feedback", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @parameters = { + feedback: { + title: "Something is weird", + message: "Hey can you look into this" + } + } + end + + context 'with valid authentication' do + context 'and valid user ID' do + it 'sends an email' do + expect { + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + }.to change { ActionMailer::Base.deliveries.count }.by 1 + end + + it 'requires a title' do + @parameters[:feedback][:title] = '' + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['error']).to eq '[title] and [message] params required.' + end + + it 'requires a message' do + @parameters[:feedback][:message] = '' + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['error']).to eq '[title] and [message] params required.' + end + + it 'handles exceptions' do + expect(FeedbackMailer).to receive(:sendFeedback).and_raise('AnException') + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['error']).to eq 'AnException' + end + end + end + + context 'with corrupted authorization' do + before do + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/feedback', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/feedback' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/groups/groups_create_spec.rb b/viscoll-api/spec/requests/groups/groups_create_spec.rb new file mode 100644 index 00000000..031d29ae --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_create_spec.rb @@ -0,0 +1,179 @@ +require 'rails_helper' + +describe "POST /groups", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user, noteTypes: ["Ink"]}) + @parameters = { + "group": { + "project_id": @project.id.to_str, + "title": "New Quire", + "direction": "left-to-right", + "type": "Quire", + }, + "additional": { + "order": 1, + "memberOrder": 1, + "noOfGroups": 1, + "noOfLeafs": 5, + "conjoin": true, + "oddMemberLeftOut": 2 + } + } + end + + context 'and valid authorization' do + context 'and standard group' do + before do + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'adds a group to the project' do + expect(@project.groups).to include an_object_having_attributes(title: "New Quire") + end + end + + context 'and as a sub-group' do + before do + @group2 = FactoryGirl.create(:quire, { title: "Existing Quire", project: @project }) + @project.add_groupIDs([@group2.id.to_s], 0) + @parameters[:additional][:parentGroupID] = @group2.id.to_s + @parameters[:additional][:order] = 2 + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group2.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'adds a group to the project' do + expect(@group2.memberIDs.length).to eq 1 + expect(@project.groups).to include an_object_having_attributes(title: "New Quire") + end + end + + context 'and missing parameter' do + before do + @parameters[:group].delete(:project_id) + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns the error message' do + expect(@body['group']['project_id']).to include("not found") + end + end + + context 'and missing project' do + before do + @parameters[:group][:project_id] += 'missing' + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns the error message' do + expect(@body['group']['project_id']).to include("project not found with id #{@project.id.to_str}missing") + end + end + + context 'and failing params for the note' do + before do + allow_any_instance_of(Group).to receive(:save).and_return(false) + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and uncaught exception' do + before do + allow_any_instance_of(Group).to receive(:save).and_raise("Exception") + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + @body = JSON.parse(response.body) + end + + it 'returns the error message' do + expect(@body['error']).to eq "Exception" + end + end + end + + context 'with corrupted authorization' do + before do + post '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/groups', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/groups', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/groups' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb b/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb new file mode 100644 index 00000000..baf0263f --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb @@ -0,0 +1,148 @@ +require 'rails_helper' + +describe "DELETE /groups", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink"], + }) + groupIDs = [] + 5.times do |n| + group = (FactoryGirl.create(:quire, { + project: @project, + title: "QUIRE #{n+1}" + })) + groupIDs.push(group.id.to_s) + end + @project.add_groupIDs(groupIDs, 0) + @project.save + @parameters = { + projectID: @project.id.to_s, + groups: [ + @project.groups[1].id.to_str, + @project.groups[2].id.to_str, + ], + } + end + + context 'with valid authorization' do + context 'and standard group specs' do + before do + delete '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'deletes only the specified groups' do + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 1") + expect(@project.groups).not_to include an_object_having_attributes(title: "QUIRE 2") + expect(@project.groups).not_to include an_object_having_attributes(title: "QUIRE 3") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 4") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 5") + end + end + + context 'and missing group' do + before do + @parameters[:groups][0] += 'missing' + @parameters[:groups][1] += 'missing' + delete "/groups", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'leaves the groups alone' do + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 1") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 2") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 3") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 4") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 5") + end + end + + context 'and unauthorized group' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + delete '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the groups alone' do + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 1") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 2") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 3") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 4") + expect(@project.groups).to include an_object_having_attributes(title: "QUIRE 5") + end + end + end + + context 'with corrupted authorization' do + before do + delete '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/groups', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/groups', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete '/groups' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/groups/groups_destroy_spec.rb b/viscoll-api/spec/requests/groups/groups_destroy_spec.rb new file mode 100644 index 00000000..75854507 --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_destroy_spec.rb @@ -0,0 +1,149 @@ +require 'rails_helper' + +describe "DELETE /groups/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink"], + }) + @groupIDs = [] + 5.times do |n| + group = FactoryGirl.create(:quire, { project: @project }) + @groupIDs.push(group.id.to_s) + end + @group = @project.groups.find(@groupIDs[3]) + @project.add_groupIDs(@groupIDs, 0) + @parameters = { + projectID: @project.id.to_s, + group: { + type: "Booklet", + title: "Changed title" + }, + } + end + + context 'with valid authorization' do + context 'and standard group specs' do + before do + delete "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'destroys the group' do + expect(@project.groups).not_to include an_object_having_attributes(id: @group.id) + end + end + + context 'and missing group' do + before do + delete "/groups/#{@group.id.to_str}missing", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'returns the right error message' do + expect(@body['error']).to eq "group not found" + end + end + + context 'and unauthorized group' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + delete "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'retains the group' do + expect(@project.groups).to include an_object_having_attributes(id: @group.id) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Group).to receive(:destroy).and_raise('MyException') + delete "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the exception' do + expect(@body['error']).to eq 'MyException' + end + end + end + + context 'with corrupted authorization' do + before do + delete "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete "/groups/#{@group.id.to_str}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb b/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb new file mode 100644 index 00000000..454f0a3e --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb @@ -0,0 +1,169 @@ +require 'rails_helper' + +describe "PUT /groups", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink"], + }) + 5.times do |n| + @project.groups << FactoryGirl.create(:quire, { + project: @project, + }) + end + @project.save + @parameters = { + groups: [ + { + id: @project.groups[1].id.to_str, + attributes: { + title: "Changed title 1" + } + }, + { + id: @project.groups[2].id.to_str, + attributes: { + title: "Changed title 2" + } + } + ], + } + end + + context 'with valid authorization' do + context 'and standard group specs' do + before do + put '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'edits the group' do + expect(@project.groups[1].title).to eq "Changed title 1" + expect(@project.groups[2].title).to eq "Changed title 2" + end + end + + context 'and missing group' do + before do + @parameters[:groups][0][:id] += 'missing' + @parameters[:groups][1][:id] += 'missing' + put "/groups", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and unauthorized group' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + put '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the targets unaltered' do + expect(@project.groups[1].title).not_to eq "Changed title 1" + expect(@project.groups[2].title).not_to eq "Changed title 2" + end + end + + context 'and failed update' do + before do + allow_any_instance_of(Group).to receive(:update).and_return(false) + put '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Group).to receive(:update).and_raise('MyException') + put '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the exception' do + expect(@body['error']).to eq 'MyException' + end + end + end + + context 'with corrupted authorization' do + before do + put '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/groups', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/groups', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/groups' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/groups/groups_update_spec.rb b/viscoll-api/spec/requests/groups/groups_update_spec.rb new file mode 100644 index 00000000..a92e782b --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_update_spec.rb @@ -0,0 +1,155 @@ +require 'rails_helper' + +describe "PUT /groups/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink"], + }) + 2.times do + @project.groups << FactoryGirl.create(:quire, { project: @project }) + end + @project.save + @group = @project.groups[0] + @parameters = { + group: { + type: "Booklet", + title: "Changed title" + }, + } + end + + context 'with valid authorization' do + context 'and standard group specs' do + before do + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'edits the group' do + expect(@group.type).to eq "Booklet" + expect(@group.title).to eq "Changed title" + end + end + + context 'and missing group' do + before do + put "/groups/#{@group.id.to_str}missing", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'returns the right error message' do + expect(@body['error']).to eq "group not found" + end + end + + context 'and unauthorized group' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + end + + context 'and failed update' do + before do + allow_any_instance_of(Group).to receive(:update).and_return(false) + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Group).to receive(:update).and_raise('MyException') + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the exception' do + expect(@body['error']).to eq 'MyException' + end + end + end + + context 'with corrupted authorization' do + before do + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put "/groups/#{@group.id.to_str}", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put "/groups/#{@group.id.to_str}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/images/destroy_images_spec.rb b/viscoll-api/spec/requests/images/destroy_images_spec.rb new file mode 100644 index 00000000..3d47825a --- /dev/null +++ b/viscoll-api/spec/requests/images/destroy_images_spec.rb @@ -0,0 +1,134 @@ +require 'rails_helper' + +describe "DELETE /images", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1, 2]]) + @image1 = FactoryGirl.create(:image, user: @user) + @image2 = FactoryGirl.create(:image, user: @user) + @parameters = { + "imageIDs": [@image1.id.to_s] + } + end + + context 'and valid authorization' do + context 'and valid image' do + before do + delete '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'deletes the right image' do + expect(Image.where(id: @image1.id)).not_to exist + expect(Image.where(id: @image2.id)).to exist + end + end + + context 'and missing image' do + before do + @parameters[:imageIDs][0] += 'missing' + delete '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'returns the error message' do + expect(@body['error']).to eq("image not found with id #{@image1.id.to_str}missing") + end + end + + context 'and unauthorized image' do + before do + @image1.update(user: FactoryGirl.create(:user)) + delete '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + end + + context 'and uncaught exception' do + before do + allow(Image).to receive(:find).and_raise("Exception") + delete '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + @body = JSON.parse(response.body) + end + + it 'returns the error message' do + expect(@body['error']).to eq "Exception" + end + end + end + + context 'with corrupted authorization' do + before do + delete '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/images', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/images', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete '/images' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/images/link_images_spec.rb b/viscoll-api/spec/requests/images/link_images_spec.rb new file mode 100644 index 00000000..f5368562 --- /dev/null +++ b/viscoll-api/spec/requests/images/link_images_spec.rb @@ -0,0 +1,253 @@ +require 'rails_helper' + +describe "PUT /images/link", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project1 = FactoryGirl.create(:project, user: @user) + @project2 = FactoryGirl.create(:project, user: @user) + @image1 = FactoryGirl.create(:pixel, user: @user) + @image2 = FactoryGirl.create(:shiba_inu, user: @user) + @parameters = { + "projectIDs": [], + "imageIDs": [] + } + end + + context 'with valid authorization' do + context 'and valid image and project' do + before do + @parameters[:projectIDs] = [@project1.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'establishes the link' do + expect(@image1.projectIDs).to include @project1.id.to_s + end + end + + context 'and multiple valid images and multiple projects' do + before do + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'establishes the link' do + expect(@image1.projectIDs).to include @project1.id.to_s + expect(@image1.projectIDs).to include @project2.id.to_s + expect(@image2.projectIDs).to include @project1.id.to_s + expect(@image2.projectIDs).to include @project2.id.to_s + end + end + + context 'and valid image but missing project' do + before do + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s+'missing'] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'shows the error' do + expect(@body['error']).to eq "project not found with id #{@project2.id}missing" + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to be_empty + expect(@image2.projectIDs).to be_empty + end + end + + context 'and valid image but unauthorized project' do + before do + @project2.update(user: FactoryGirl.create(:user)) + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to be_empty + expect(@image2.projectIDs).to be_empty + end + end + + context 'and missing image but valid project' do + before do + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s+'missing'] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'shows the error' do + expect(@body['error']).to eq "image not found with id #{@image2.id}missing" + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to be_empty + expect(@image2.projectIDs).to be_empty + end + end + + context 'and unauthorized image but valid project' do + before do + @image2.update(user: FactoryGirl.create(:user)) + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to be_empty + expect(@image2.projectIDs).to be_empty + end + end + + context 'and exception in projects' do + before do + allow(Project).to receive(:find).and_raise('waahooexception') + @parameters[:projectIDs] = [@project1.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the error' do + expect(@body['error']).to eq 'waahooexception' + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to be_empty + expect(@image2.projectIDs).to be_empty + end + end + + context 'and exception in images' do + before do + allow(Image).to receive(:find).and_raise('waahooexception') + @parameters[:projectIDs] = [@project1.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s] + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the error' do + expect(@body['error']).to eq 'waahooexception' + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to be_empty + expect(@image2.projectIDs).to be_empty + end + end + end + + context 'with corrupted authorization' do + before do + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/images/link', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/images/link' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end + diff --git a/viscoll-api/spec/requests/images/show_images_spec.rb b/viscoll-api/spec/requests/images/show_images_spec.rb new file mode 100644 index 00000000..02a8d414 --- /dev/null +++ b/viscoll-api/spec/requests/images/show_images_spec.rb @@ -0,0 +1,74 @@ +require 'rails_helper' + +describe "GET /images/:id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1, 2]]) + @image1 = FactoryGirl.create(:pixel, user: @user) + @image2 = FactoryGirl.create(:shiba_inu, user: @user) + end + + before :all do + imagePath = "#{Rails.root}/public/uploads" + File.new(imagePath+'/pixel', 'w') + end + + after :all do + imagePath = "#{Rails.root}/public/uploads" + File.delete(imagePath+'/pixel') + end + + context 'and valid authorization' do + context 'and valid image' do + before do + get "/images/#{@image1.id}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'shows the right image' do + expect(response.body).to eq(File.open("#{Rails.root}/public/uploads/pixel", 'rb') { |file| file.read }) + end + end + + context 'and missing image' do + before do + get "/images/#{@image1.id}missing", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'returns the error message' do + expect(@body['error']).to eq("image not found with id #{@image1.id.to_str}missing") + end + end + + context 'and uncaught exception' do + before do + allow(Image).to receive(:find).and_raise("Exception") + get "/images/#{@image1.id}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + @body = JSON.parse(response.body) + end + + it 'returns the error message' do + expect(@body['error']).to eq "Exception" + end + end + end +end diff --git a/viscoll-api/spec/requests/images/unlink_images_spec.rb b/viscoll-api/spec/requests/images/unlink_images_spec.rb new file mode 100644 index 00000000..4fa01ebc --- /dev/null +++ b/viscoll-api/spec/requests/images/unlink_images_spec.rb @@ -0,0 +1,259 @@ +require 'rails_helper' + +describe "PUT /images/unlink", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project1 = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[2, 2]]) + @project2 = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[2, 2]]) + @image1 = FactoryGirl.create(:pixel, user: @user, projectIDs: [@project1.id.to_s, @project2.id.to_s], sideIDs: [@project1.leafs[0].rectoID, @project2.leafs[0].rectoID]) + @image2 = FactoryGirl.create(:shiba_inu, user: @user, projectIDs: [@project1.id.to_s, @project2.id.to_s], sideIDs: [@project1.leafs[0].versoID, @project2.leafs[0].versoID]) + @parameters = { + "projectIDs": [], + "imageIDs": [] + } + end + + context 'with valid authorization' do + context 'and valid image and project' do + before do + @parameters[:projectIDs] = [@project1.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'breaks the right link' do + expect(@image1.projectIDs).not_to include @project1.id.to_s + expect(@image1.projectIDs).to include @project2.id.to_s + expect(@image1.sideIDs).to eq [@project2.leafs[0].rectoID] + expect(Side.find(@project1.leafs[0].rectoID).image).to eq({}) + end + end + + context 'and multiple valid images and multiple projects' do + before do + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + # If images have no projectIDs, it will be deleted after unlinking + # @image1.reload + # @image2.reload + @user.reload + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'breaks the specified links' do + expect(@user.images).to_not be_empty + expect(Side.find(@project1.leafs[0].rectoID).image).to eq({}) + expect(Side.find(@project1.leafs[0].versoID).image).to eq({}) + expect(Side.find(@project2.leafs[0].rectoID).image).to eq({}) + expect(Side.find(@project2.leafs[0].versoID).image).to eq({}) + end + end + + context 'and valid image but missing project' do + before do + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s+'missing'] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'shows the error' do + expect(@body['error']).to eq "project not found with id #{@project2.id}missing" + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + expect(@image2.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + end + end + + context 'and valid image but unauthorized project' do + before do + @project2.update(user: FactoryGirl.create(:user)) + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + expect(@image2.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + end + end + + context 'and missing image but valid project' do + before do + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s+'missing'] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'shows the error' do + expect(@body['error']).to eq "image not found with id #{@image2.id}missing" + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + expect(@image2.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + end + end + + context 'and unauthorized image but valid project' do + before do + @image2.update(user: FactoryGirl.create(:user)) + @parameters[:projectIDs] = [@project1.id.to_s, @project2.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s, @image2.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @image2.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + expect(@image2.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + end + end + + context 'and exception in projects' do + before do + allow(Project).to receive(:find).and_raise('waahooexception') + @parameters[:projectIDs] = [@project1.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the error' do + expect(@body['error']).to eq 'waahooexception' + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + expect(@image2.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + end + end + + context 'and exception in images' do + before do + allow(Image).to receive(:find).and_raise('waahooexception') + @parameters[:projectIDs] = [@project1.id.to_s] + @parameters[:imageIDs] = [@image1.id.to_s] + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @image1.reload + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the error' do + expect(@body['error']).to eq 'waahooexception' + end + + it 'leaves the images alone' do + expect(@image1.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + expect(@image2.projectIDs).to eq [@project1.id.to_s, @project2.id.to_s] + end + end + end + + context 'with corrupted authorization' do + before do + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/images/unlink', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/images/unlink' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end + diff --git a/viscoll-api/spec/requests/images/upload_images_spec.rb b/viscoll-api/spec/requests/images/upload_images_spec.rb new file mode 100644 index 00000000..008b1dbf --- /dev/null +++ b/viscoll-api/spec/requests/images/upload_images_spec.rb @@ -0,0 +1,180 @@ +require 'rails_helper' + +describe "POST /images", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1, 2]]) + @parameters = { + "projectID": @project.id.to_s, + "images": [ + { + "filename": "green", + "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" + }, + { + "filename": "blue", + "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" + } + ] + } + end + + after do + Image.where(:projectIDs => @project.id.to_s).each do | image | + image.destroy + end + end + + context 'and valid authorization' do + context 'and standard group' do + before do + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'creates two new images connected to the project' do + expect(Image.where(filename: 'green.png')).to exist + expect(Image.where(filename: 'blue.png')).to exist + expect(Image.find_by(filename: 'green.png').projectIDs).to include @project.id.to_s + expect(Image.find_by(filename: 'blue.png').projectIDs).to include @project.id.to_s + end + end + + context 'and duplicated image' do + before do + @parameters[:images][1][:filename] = 'green' + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'creates two new images, the second with the _copy(n) suffix' do + expect(Image.where(filename: 'green.png')).to exist + expect(Image.where(filename: 'green_copy(1).png')).to exist + expect(Image.find_by(filename: 'green.png').projectIDs).to include @project.id.to_s + expect(Image.find_by(filename: 'green_copy(1).png').projectIDs).to include @project.id.to_s + end + end + + context 'and missing project' do + before do + @parameters[:projectID] += 'missing' + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'returns the error message' do + expect(@body['error']).to eq("project not found with id #{@project.id.to_str}missing") + end + end + + context 'and unauthorized project' do + before do + @project.update(user: FactoryGirl.create(:user)) + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + end + + context 'and failing image' do + before do + allow_any_instance_of(Image).to receive(:valid?).and_return(false) + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and uncaught exception' do + before do + allow(Project).to receive(:find).and_raise("Exception") + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + @body = JSON.parse(response.body) + end + + it 'returns the error message' do + expect(@body['error']).to eq "Exception" + end + end + end + + context 'with corrupted authorization' do + before do + post '/images', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/images', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/images', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/images' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/images/zip_images_spec.rb b/viscoll-api/spec/requests/images/zip_images_spec.rb new file mode 100644 index 00000000..e56ce3d4 --- /dev/null +++ b/viscoll-api/spec/requests/images/zip_images_spec.rb @@ -0,0 +1,66 @@ +require 'rails_helper' + +describe "GET /images/zip/:imageid_:projectid", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + @zipPath = "#{Rails.root}/public/uploads" + end + + before :each do + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1, 2]]) + @image1 = FactoryGirl.create(:pixel, user: @user) + @image2 = FactoryGirl.create(:shiba_inu, user: @user) + end + + context 'and valid authorization' do + context 'and valid image' do + before do + + File.open("#{@zipPath}/#{@project.id}_images.zip", 'w+') { |file| file.write('testcontent') } + get "/images/zip/#{@project.id}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + end + after do + File.delete("#{@zipPath}/#{@project.id}_images.zip") + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'sends the zip file' do + expect(response.body).to eq('testcontent') + end + end + + context 'and missing image' do + before do + get "/images/zip/#{@image1.id}missing_#{@project.id}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and uncaught exception' do + before do + allow(Image).to receive(:find).and_raise("Exception") + get "/images/zip/#{@image1.id}_#{@project.id}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + @body = JSON.parse(response.body) + end + + it 'returns the error message' do + expect(@body['error']).to include "Cannot read file" + end + end + end +end diff --git a/viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb b/viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb new file mode 100644 index 00000000..fe336020 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb @@ -0,0 +1,209 @@ +require 'rails_helper' + +describe "PUT /leafs/conjoin", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + leaf_count = 5 + @project = FactoryGirl.create(:project, user: @user) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leafs = leaf_count.times.collect { FactoryGirl.create(:leaf, project: @project, material: 'Parchment', parentID: @group.id.to_s) } + @group.add_members(@leafs.collect { |leaf| leaf.id.to_s }, 0) + @parameters = { + "leafs": @leafs[0..3].collect { |leaf| leaf.id.to_s } + } + end + + context 'and valid authorization' do + context 'and valid even number of leafs' do + before do + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'updates the affected leafs' do + expect(@leafs[0].conjoined_to).to eq @leafs[3].id.to_s + expect(@leafs[1].conjoined_to).to eq @leafs[2].id.to_s + expect(@leafs[2].conjoined_to).to eq @leafs[1].id.to_s + expect(@leafs[3].conjoined_to).to eq @leafs[0].id.to_s + end + end + + context 'and valid odd number of leafs' do + before do + @parameters[:leafs] = @leafs[0..4].collect { |leaf| leaf.id.to_s } + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'updates the affected leafs' do + expect(@leafs[0].conjoined_to).to eq @leafs[4].id.to_s + expect(@leafs[1].conjoined_to).to eq @leafs[3].id.to_s + expect(@leafs[2].conjoined_to).to be_blank + expect(@leafs[3].conjoined_to).to eq @leafs[1].id.to_s + expect(@leafs[4].conjoined_to).to eq @leafs[0].id.to_s + end + end + + context 'and valid odd subleaves within even conjoined quire' do + before do + @project = FactoryGirl.create(:codex_project, user: @user, quire_structure: [[1,8]]) + @leafs = @project.leafs + @parameters[:leafs] = @leafs[0..4].collect { |leaf| leaf.id.to_s } + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'updates the affected leafs' do + expect(@leafs[0].conjoined_to).to eq @leafs[4].id.to_s + expect(@leafs[1].conjoined_to).to eq @leafs[3].id.to_s + expect(@leafs[2].conjoined_to).to be_blank + expect(@leafs[3].conjoined_to).to eq @leafs[1].id.to_s + expect(@leafs[4].conjoined_to).to eq @leafs[0].id.to_s + expect(@leafs[5].conjoined_to).to be_blank + expect(@leafs[6].conjoined_to).to be_blank + expect(@leafs[7].conjoined_to).to be_blank + end + + end + + context 'and too few leafs' do + before do + @parameters[:leafs] = [@leafs[0].id.to_s] + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'explains the error' do + expect(JSON.parse(response.body)['leafs']).to include 'Minimum of 2 leaves required to conjoin' + end + end + + context 'and missing leaf' do + before do + @parameters[:leafs][0] += 'missing' + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Leaf).to receive(:save).and_raise('MyException') + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized page' do + before do + @user2 = FactoryGirl.create(:user) + @project.update(user: @user2) + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not edit the leaf' do + @leafs.each do |leaf| + expect(leaf.conjoined_to).to be_blank + end + end + end + end + + context 'with corrupted authorization' do + before do + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/leafs/conjoin', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/leafs/leafs_create_spec.rb b/viscoll-api/spec/requests/leafs/leafs_create_spec.rb new file mode 100644 index 00000000..69974d45 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_create_spec.rb @@ -0,0 +1,160 @@ +require 'rails_helper' + +describe "POST /leafs", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, user: @user) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @parameters = { + "leaf": { + "project_id": @project.id.to_s, + "parentID": @group.id.to_s, + "order": 1, + "material": "Parchment", + }, + "additional": { + "memberOrder": 1, + "noOfLeafs": 5, + "conjoin": true, + "oddMemberLeftOut": 2 + } + } + end + + it 'should set up properly' do + expect(true).to be true + end + + context 'and valid authorization' do + context 'and standard leaf' do + before do + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'adds 5 leafs to the project and group' do + expect(@project.leafs.length).to eq 5 + expect(@group.memberIDs).to eq(@project.leafs.collect { |leaf| leaf.id.to_s }) + end + end + + context 'and missing project' do + before do + @parameters[:leaf][:project_id] += "WAAHOO" + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and invalid additional arguments' do + before do + @parameters[:additional][:noOfLeafs] = -1 + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and failing params for the leaf' do + before do + allow_any_instance_of(Leaf).to receive(:save).and_return(false) + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized project' do + before do + @user2 = FactoryGirl.create(:user) + @project.update(user: @user2) + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not add leafs to the project' do + expect(@project.leafs).to be_blank + expect(@group.memberIDs).to be_blank + end + end + end + + context 'with corrupted authorization' do + before do + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/leafs', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/leafs' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb b/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb new file mode 100644 index 00000000..256c4987 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb @@ -0,0 +1,178 @@ +require 'rails_helper' + +describe "DELETE /leafs", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + leaf_count = 4 + @project = FactoryGirl.create(:project, user: @user) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leafs = leaf_count.times.collect { FactoryGirl.create(:leaf, project: @project, material: 'Parchment', parentID: @group.id.to_s) } + leaf_count.times.each do |i| + params = { + conjoined_to: @leafs[-i-1].id.to_s + } + unless i == 0 + params[:attached_above] = @leafs[i-1].id.to_s + end + unless i == leaf_count-1 + params[:attached_below] = @leafs[i+1].id.to_s + end + @leafs[i].update(params) + end + @group.add_members(@leafs.collect { |leaf| leaf.id.to_s }, 0) + @parameters = { + "leafs": [ + @leafs[1].id.to_s, + @leafs[0].id.to_s + ] + } + end + + it 'should set up properly' do + expect(true).to be true + expect(@leafs[0].conjoined_to).to eq @leafs[3].id.to_s + expect(@leafs[1].conjoined_to).to eq @leafs[2].id.to_s + expect(@leafs[2].conjoined_to).to eq @leafs[1].id.to_s + expect(@leafs[3].conjoined_to).to eq @leafs[0].id.to_s + expect(@leafs[1].attached_above).to eq @leafs[0].id.to_s + expect(@leafs[2].attached_above).to eq @leafs[1].id.to_s + expect(@leafs[3].attached_above).to eq @leafs[2].id.to_s + expect(@leafs[0].attached_below).to eq @leafs[1].id.to_s + expect(@leafs[1].attached_below).to eq @leafs[2].id.to_s + expect(@leafs[2].attached_below).to eq @leafs[3].id.to_s + end + + context 'and valid authorization' do + context 'and standard leaf' do + before do + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each do |leaf| + if Leaf.where(id: leaf.id).exists? + leaf.reload + end + end + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'deletes the specified leafs' do + expect(Leaf.where(id: @leafs[0].id.to_s).exists?).to be false + expect(Leaf.where(id: @leafs[1].id.to_s).exists?).to be false + expect(Leaf.where(id: @leafs[2].id.to_s).exists?).to be true + expect(Leaf.where(id: @leafs[3].id.to_s).exists?).to be true + end + end + + context 'and missing page' do + before do + @parameters[:leafs][0] += 'missing' + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Leaf).to receive(:destroy).and_raise('MyException') + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized page' do + before do + @user2 = FactoryGirl.create(:user) + @project.update(user: @user2) + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each do |leaf| + if Leaf.where(id: leaf.id).exists? + leaf.reload + end + end + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not remove any leafs' do + 4.times.each do |i| + expect(Leaf.where(id: @leafs[i].id).exists?).to be true + end + end + end + end + + context 'with corrupted authorization' do + before do + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/leafs', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb b/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb new file mode 100644 index 00000000..46fbe01f --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb @@ -0,0 +1,166 @@ +require 'rails_helper' + +describe "DELETE /leafs/:id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + leaf_count = 4 + @project = FactoryGirl.create(:project, user: @user) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leafs = leaf_count.times.collect { FactoryGirl.create(:leaf, project: @project, material: 'Parchment', parentID: @group.id.to_s) } + leaf_count.times.each do |i| + params = { + conjoined_to: @leafs[-i-1].id.to_s + } + unless i == 0 + params[:attached_above] = @leafs[i-1].id.to_s + end + unless i == leaf_count-1 + params[:attached_below] = @leafs[i+1].id.to_s + end + @leafs[i].update(params) + end + @group.add_members(@leafs.collect { |leaf| leaf.id.to_s }, 0) + end + + it 'should set up properly' do + expect(true).to be true + expect(@leafs[0].conjoined_to).to eq @leafs[3].id.to_s + expect(@leafs[1].conjoined_to).to eq @leafs[2].id.to_s + expect(@leafs[2].conjoined_to).to eq @leafs[1].id.to_s + expect(@leafs[3].conjoined_to).to eq @leafs[0].id.to_s + expect(@leafs[1].attached_above).to eq @leafs[0].id.to_s + expect(@leafs[2].attached_above).to eq @leafs[1].id.to_s + expect(@leafs[3].attached_above).to eq @leafs[2].id.to_s + expect(@leafs[0].attached_below).to eq @leafs[1].id.to_s + expect(@leafs[1].attached_below).to eq @leafs[2].id.to_s + expect(@leafs[2].attached_below).to eq @leafs[3].id.to_s + end + + context 'and valid authorization' do + context 'and standard leaf' do + before do + delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload unless leaf.id == @leafs[1].id } + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'remove the leaf' do + expect(Leaf.where(id: @leafs[1].id).exists?).to be false + end + + it 'frees the conjoined leaf' do + expect(@leafs[2].conjoined_to).to be_blank + end + + it 'frees attached leafs' do + expect(@leafs[0].attached_below).to eq 'None' + expect(@leafs[2].attached_above).to eq 'None' + end + end + + context 'and missing page' do + before do + delete "/leafs/#{@leafs[1].id.to_s}waahoo", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Leaf).to receive(:destroy).and_raise('MyException') + delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized page' do + before do + @user2 = FactoryGirl.create(:user) + @project.update(user: @user2) + delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not remove any' do + expect(@project.leafs.count).to eq 4 + end + end + end + + context 'with corrupted authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb b/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb new file mode 100644 index 00000000..5a8627c7 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb @@ -0,0 +1,198 @@ +require 'rails_helper' + +describe "PUT /leafs", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + leaf_count = 4 + @project = FactoryGirl.create(:project, user: @user) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leafs = leaf_count.times.collect { FactoryGirl.create(:leaf, project: @project, material: 'Parchment', parentID: @group.id.to_s) } + leaf_count.times.each do |i| + params = { + conjoined_to: @leafs[-i-1].id.to_s + } + unless i == 0 + params[:attached_above] = @leafs[i-1].id.to_s + end + unless i == leaf_count-1 + params[:attached_below] = @leafs[i+1].id.to_s + end + @leafs[i].update(params) + end + @group.add_members(@leafs.collect { |leaf| leaf.id.to_s }, 0) + @parameters = { + "leafs": [ + { + "id": @leafs[1].id.to_s, + "attributes": { + "material": "Paper", + "type": "Added", + "attached_above": @leafs[0].id.to_s, + "attached_below": @leafs[2].id.to_s + } + } + ], + "project_id": @project.id.to_s + } + end + + it 'should set up properly' do + expect(true).to be true + expect(@leafs[0].conjoined_to).to eq @leafs[3].id.to_s + expect(@leafs[1].conjoined_to).to eq @leafs[2].id.to_s + expect(@leafs[2].conjoined_to).to eq @leafs[1].id.to_s + expect(@leafs[3].conjoined_to).to eq @leafs[0].id.to_s + expect(@leafs[1].attached_above).to eq @leafs[0].id.to_s + expect(@leafs[2].attached_above).to eq @leafs[1].id.to_s + expect(@leafs[3].attached_above).to eq @leafs[2].id.to_s + expect(@leafs[0].attached_below).to eq @leafs[1].id.to_s + expect(@leafs[1].attached_below).to eq @leafs[2].id.to_s + expect(@leafs[2].attached_below).to eq @leafs[3].id.to_s + end + + context 'and valid authorization' do + context 'and standard leaf' do + before do + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'updates the leaf' do + expect(@leafs[1].material).to eq 'Paper' + expect(@leafs[1].type).to eq 'Added' + end + end + + context 'and missing project' do + before do + @parameters[:project_id] += 'missing' + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and missing page' do + before do + @parameters[:leafs][0][:id] += 'missing' + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and failed save' do + before do + allow_any_instance_of(Leaf).to receive(:update).and_return(false) + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and raised exception' do + before do + allow_any_instance_of(Leaf).to receive(:update).and_raise('MyException') + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized page' do + before do + @user2 = FactoryGirl.create(:user) + @project.update(user: @user2) + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not edit the leaf' do + expect(@leafs[1].material).not_to eq 'Paper' + expect(@leafs[1].type).not_to eq 'Added' + end + end + end + + context 'with corrupted authorization' do + before do + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/leafs', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete "/leafs/#{@leafs[1].id.to_s}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/leafs/leafs_update_spec.rb b/viscoll-api/spec/requests/leafs/leafs_update_spec.rb new file mode 100644 index 00000000..44b0b734 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_update_spec.rb @@ -0,0 +1,149 @@ +require 'rails_helper' + +describe "PUT /leafs/:id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, user: @user) + @group = FactoryGirl.create(:group, project: @project) + @project.add_groupIDs([@group.id.to_s], 0) + @leafs = 3.times.collect { FactoryGirl.create(:leaf, project: @project, material: 'Parchment', parentID: @group.id.to_s) } + @group.add_members(@leafs.collect { |leaf| leaf.id.to_s }, 0) + @parameters = { + "leaf": { + "material": "Paper", + "type": "Added", + "conjoined_to": @leafs[2].id.to_s, + "attached_below": "Sewn" + } + } + end + + it 'should set up properly' do + expect(true).to be true + end + + context 'and valid authorization' do + context 'and standard leaf' do + before do + put "/leafs/#{@leafs[0].id.to_s}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'edit and reconjoin the leaf' do + expect(@leafs[0].material).to eq 'Paper' + expect(@leafs[0].conjoined_to).to eq @leafs[2].id.to_s + expect(@leafs[0].attached_below).to eq 'Sewn' + expect(@leafs[1].attached_above).to eq 'Sewn' + expect(@leafs[2].conjoined_to).to eq @leafs[0].id.to_s + end + end + + context 'and missing page' do + before do + put "/leafs/#{@leafs[0].id.to_s}waahoo", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context 'and failing params for the leaf' do + before do + allow_any_instance_of(Leaf).to receive(:update).and_return(false) + put "/leafs/#{@leafs[0].id.to_s}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized page' do + before do + @user2 = FactoryGirl.create(:user) + @project.update(user: @user2) + put "/leafs/#{@leafs[0].id.to_s}", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not edit the leaf' do + expect(@leafs[0].material).not_to eq 'Paper' + expect(@leafs[0].conjoined_to).not_to eq @leafs[2].id.to_s + expect(@leafs[2].conjoined_to).not_to eq @leafs[0].id.to_s + end + end + end + + context 'with corrupted authorization' do + before do + put "/leafs/#{@leafs[0].id.to_s}", params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put "/leafs/#{@leafs[0].id.to_s}", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put "/leafs/#{@leafs[0].id.to_s}", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put "/leafs/#{@leafs[0].id.to_s}" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_create_spec.rb b/viscoll-api/spec/requests/notes/notes_create_spec.rb new file mode 100644 index 00000000..3c23e78e --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_create_spec.rb @@ -0,0 +1,152 @@ +require 'rails_helper' + +describe "POST /notes", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user, noteTypes: ["Ink"]}) + @parameters = { + "note": { + "project_id": @project.id.to_str, + "title": "some title for note", + "type": "Ink", + "description": "blue ink" + } + } + end + + context 'and valid authorization' do + context 'and standard notes' do + before do + post '/notes', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'adds a note to the project' do + expect(@project.notes.length).to eq 1 + expect(@project.notes[0].title).to eq "some title for note" + end + end + + context 'and out-of-context notes' do + before do + @parameters[:note][:type] = "WAAHOO" + post '/notes', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'says what types are allowed' do + expect(@body['type']).to include('should be one of ["Ink"]') + end + end + + context 'and missing project' do + before do + @parameters[:note][:project_id] += "WAAHOO" + post '/notes', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'gives the right error message' do + expect(@body['project_id']).to eq "project not found with id #{@parameters[:note][:project_id]}" + end + end + + context 'and failing params for the note' do + before do + allow_any_instance_of(Note).to receive(:save).and_return(false) + post '/notes', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an unauthorized project' do + before do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { user: @user2, noteTypes: ["Ink"] }) + @parameters[:note][:project_id] = @project2.id.to_str + post '/notes', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not add notes to the project' do + expect(@project2.notes).not_to include an_object_having_attributes({ title: "some title for note" }) + end + end + end + + context 'with corrupted authorization' do + before do + post '/notes', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/notes', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/notes', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/notes' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_create_type_spec.rb b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb new file mode 100644 index 00000000..d0f2ce3c --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb @@ -0,0 +1,147 @@ +require 'rails_helper' + +describe "POST /notes/type", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user, noteTypes: ["Ink"]}) + @parameters = { + "noteType": { + "project_id": @project.id.to_str, + "type": "Paper" + } + } + end + + context 'with valid authorization' do + context 'with valid parameters' do + before do + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should add the type to the project' do + expect(@project.noteTypes).to include "Ink" + expect(@project.noteTypes).to include "Paper" + end + end + + context 'with missing project' do + before do + @parameters[:noteType][:project_id] += 'missing' + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['project_id']).to eq "project not found with id #{@project.id}missing" + end + end + + context 'with duplicated type' do + before do + @parameters[:noteType][:type] = "Ink" + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['type']).to eq "Ink type already exists in the project" + end + + it 'should leave the project alone' do + expect(@project.noteTypes).to eq ["Ink"] + end + end + + context 'with unauthorized project' do + before do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, {user: @user2, noteTypes: ["Ink"]}) + @parameters[:noteType][:project_id] = @project2.id.to_str + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project2.reload + end + + it 'should return 403' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the types alone' do + expect(@project2.noteTypes).not_to include("Paper") + end + end + end + + context 'with corrupted authorization' do + before do + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/notes/type', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/notes/type' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb new file mode 100644 index 00000000..fb8a3481 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb @@ -0,0 +1,163 @@ +require 'rails_helper' + +describe "DELETE /notes/type", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user, noteTypes: ["Ink", "Paper"]}) + @project.notes << FactoryGirl.create(:note, { + project_id: @project.id, + type: "Ink", + description: "Sepia" + }) + @project.notes << FactoryGirl.create(:note, { + project_id: @project.id, + type: "Paper", + description: "Parchment" + }) + @project.save + @parameters = { + "noteType": { + "project_id": @project.id.to_str, + "type": "Ink" + } + } + end + + context 'with valid authorization' do + context 'with valid parameters' do + before do + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should remove the type from the project' do + expect(@project.noteTypes).not_to include "Ink" + expect(@project.noteTypes).to include "Paper" + end + + it 'should change notes of the type to Unknown' do + expect(@project.notes).to include an_object_having_attributes(type: "Unknown") + expect(@project.notes).to include an_object_having_attributes(type: "Paper") + end + end + + context 'with missing project' do + before do + @parameters[:noteType][:project_id] += 'missing' + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['project_id']).to eq "project not found with id #{@project.id}missing" + end + end + + context 'with out-of-context type' do + before do + @parameters[:noteType][:type] = "Waahoo" + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['type']).to eq "Waahoo type doesn't exist in the project" + end + + it 'should leave the project alone' do + expect(@project.noteTypes).to eq ["Ink", "Paper"] + end + end + + context 'with unauthorized project' do + before do + @user2 = FactoryGirl.create(:user) + @project.user = @user2 + @project.save + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'should return 403' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the types alone' do + expect(@project.noteTypes).to eq ["Ink", "Paper"] + end + end + end + + context 'with corrupted authorization' do + before do + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/notes/type', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete '/notes/type' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_destroy_spec.rb b/viscoll-api/spec/requests/notes/notes_destroy_spec.rb new file mode 100644 index 00000000..1fd6c064 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_destroy_spec.rb @@ -0,0 +1,124 @@ +require 'rails_helper' + +describe "DELETE /notes/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink"] + }) + @note = FactoryGirl.create(:note, { + type: "Ink", + project: @project + }) + @parameters = {} + end + + context 'with valid authorization' do + context 'and valid note ID' do + before do + delete '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'deletes the note' do + expect(Note.where(id: @note.id).exists?).to be false + end + end + + context 'and invalid note ID' do + before do + delete '/notes/'+@note.id+'invalid', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context "and someone else's notes" do + before do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { + user: @user2, + noteTypes: ["Hand"] + }) + @note2 = FactoryGirl.create(:note, { + type: "Hand", + project: @project2 + }) + delete '/notes/'+@note2.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the note alone' do + expect(Note.where(id: @note2.id).exists?).to be true + end + end + end + + context 'with corrupted authorization' do + before do + delete '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete '/notes/'+@note.id + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_link_spec.rb b/viscoll-api/spec/requests/notes/notes_link_spec.rb new file mode 100644 index 00000000..8d3e4a97 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_link_spec.rb @@ -0,0 +1,310 @@ +require 'rails_helper' + +describe "PUT /notes/id/link", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user, noteTypes: ["Ink"]}) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@defaultGroup.id.to_s], 0) + @note = FactoryGirl.create(:note, { + project: @project, + title: "some title for note", + type: "Ink", + description: "blue ink" + }) + @parameters = { + objects: [ + { + id: "something", + type: "Group" + } + ] + } + end + + context 'with valid authorization' do + context 'and correct group target' do + before do + @group = FactoryGirl.create(:group, { project: @project }) + @project.add_groupIDs([@group.id.to_s], 0) + @parameters = { + objects: [ + { + id: @group.id.to_str, + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should add the note to the target' do + expect(@group.notes.length).to eq 1 + expect(@group.notes[0].id).to eq @note.id + end + end + context 'and correct leaf target' do + before do + @leaf = FactoryGirl.create(:leaf, { project: @project, parentID: @defaultGroup.id.to_s }) + @defaultGroup.add_members([@leaf.id.to_s], 1) + @parameters = { + objects: [ + { + id: @leaf.id.to_str, + type: "Leaf" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @leaf.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should add the note to the target' do + expect(@leaf.notes.length).to eq 1 + expect(@leaf.notes[0].id).to eq @note.id + end + end + context 'and correct side target' do + before do + @leaf = FactoryGirl.create(:leaf, { project: @project }) + @defaultGroup.add_members([@leaf.id.to_s], 1) + @side = @project.sides[0] + @parameters = { + objects: [ + { + id: @side.id.to_str, + type: "Recto" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should add the note to the target' do + expect(@side.notes.length).to eq 1 + expect(@side.notes[0].id).to eq @note.id + end + end + context 'and a project belonging to another user' do + before :each do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { user: @user2, noteTypes: ["Ink"] }) + end + context 'and group target' do + before do + @group2 = FactoryGirl.create(:group, { project: @project2 }) + @parameters2 = { + objects: [ + { + id: @group2.id.to_str, + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters2.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group2.reload + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the group alone' do + expect(@group2.notes).to be_empty + end + end + context 'and a leaf target' do + before do + @leaf2 = FactoryGirl.create(:leaf, { project: @project2 }) + @defaultGroup.add_members([@leaf2.id.to_s], 1) + @parameters2 = { + objects: [ + { + id: @leaf2.id.to_str, + type: "Leaf" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters2.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @leaf2.reload + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the leaf alone' do + expect(@leaf2.notes).to be_empty + end + end + context 'and a side target' do + before do + @leaf2 = FactoryGirl.create(:leaf, { project: @project2 }) + @defaultGroup.add_members([@leaf2.id.to_s], 1) + @side2 = @project2.sides[0] + @parameters2 = { + objects: [ + { + id: @side2.id.to_str, + type: "Recto" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters2.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side2.reload + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the side alone' do + expect(@side2.notes).to be_empty + end + end + end + context 'and unknown target type' do + before do + @parameters[:objects][0][:type] = "unknown" + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status :unprocessable_entity + end + + it 'should give the right error message' do + expect(@body['type']).to eq "object not found with type unknown" + end + end + context 'and missing note' do + before do + put '/notes/'+@note.id+'missing/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 404' do + expect(response).to have_http_status :not_found + end + end + context 'and missing target' do + before do + @group = FactoryGirl.create(:group, { project: @project }) + @parameters = { + objects: [ + { + id: @group.id.to_str+"weird", + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status :unprocessable_entity + end + + it 'should give the right error message' do + expect(@body['id']).to eq "Group object not found with id #{@group.id.to_str}weird" + end + end + context 'and uncaught exception' do + before do + allow_any_instance_of(Note).to receive(:save).and_raise("Exception!") + @group = FactoryGirl.create(:group, { project: @project }) + @parameters = { + objects: [ + { + id: @group.id.to_str, + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status :unprocessable_entity + end + end + end + + context 'with corrupted authorization' do + before do + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/notes/'+@note.id+'/link', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/notes/'+@note.id+'/link' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_unlink_spec.rb b/viscoll-api/spec/requests/notes/notes_unlink_spec.rb new file mode 100644 index 00000000..d8d8c8cd --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_unlink_spec.rb @@ -0,0 +1,307 @@ +require 'rails_helper' + +describe "PUT /notes/id/unlink", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user, noteTypes: ["Ink"]}) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@defaultGroup.id.to_s], 0) + @note = FactoryGirl.create(:note, { + project: @project, + title: "some title for note", + type: "Ink", + description: "blue ink" + }) + @parameters = { + objects: [ + { + id: "something", + type: "Group" + } + ] + } + end + + context 'with valid authorization' do + context 'and correct group target' do + before do + @group = FactoryGirl.create(:group, { project: @project, notes: [@note] }) + @project.add_groupIDs([@group.id.to_s], 0) + @parameters = { + objects: [ + { + id: @group.id.to_str, + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should remove the note from the target' do + expect(@group.notes).to be_empty + end + end + context 'and correct leaf target' do + before do + @leaf = FactoryGirl.create(:leaf, { project: @project, notes: [@note] }) + @defaultGroup.add_members([@leaf.id.to_s], 1) + @parameters = { + objects: [ + { + id: @leaf.id.to_str, + type: "Leaf" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @leaf.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should remove the note from the target' do + expect(@leaf.notes).to be_empty + end + end + context 'and correct side target' do + before do + @leaf = FactoryGirl.create(:leaf, { project: @project, notes: [@note] }) + @defaultGroup.add_members([@leaf.id.to_s], 1) + @side = @project.sides.find(@leaf.rectoID) + @side.notes << @note + @side.save + @parameters = { + objects: [ + { + id: @side.id.to_str, + type: "Recto" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should remove the note from the target' do + expect(@side.notes).to be_empty + end + end + context 'and a project belonging to another user' do + before :each do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { user: @user2, noteTypes: ["Ink"] }) + end + context 'and group target' do + before do + @group2 = FactoryGirl.create(:group, { project: @project2 }) + @parameters2 = { + objects: [ + { + id: @group2.id.to_str, + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters2.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group2.reload + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the group alone' do + expect(@group2.notes).to be_empty + end + end + context 'and a leaf target' do + before do + @leaf2 = FactoryGirl.create(:leaf, { project: @project2 }) + @defaultGroup.add_members([@leaf2.id.to_s], 1) + @parameters2 = { + objects: [ + { + id: @leaf2.id.to_str, + type: "Leaf" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters2.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @leaf2.reload + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the leaf alone' do + expect(@leaf2.notes).to be_empty + end + end + context 'and a side target' do + before do + @side2 = FactoryGirl.create(:side, { project: @project2 }) + @parameters2 = { + objects: [ + { + id: @side2.id.to_str, + type: "Recto" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters2.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side2.reload + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the side alone' do + expect(@side2.notes).to be_empty + end + end + end + context 'and unknown target type' do + before do + @parameters[:objects][0][:type] = "unknown" + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status :unprocessable_entity + end + + it 'should give the right error message' do + expect(@body['type']).to eq "object not found with type unknown" + end + end + context 'and missing note' do + before do + put '/notes/'+@note.id+'missing/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 404' do + expect(response).to have_http_status :not_found + end + end + context 'and missing target' do + before do + @group = FactoryGirl.create(:group, { project: @project }) + @parameters = { + objects: [ + { + id: @group.id.to_str+"weird", + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status :unprocessable_entity + end + + it 'should give the right error message' do + expect(@body['id']).to eq "Group object not found with id #{@group.id.to_str}weird" + end + end + context 'and uncaught exception' do + before do + allow_any_instance_of(Note).to receive(:save).and_raise("Exception!") + @group = FactoryGirl.create(:group, { project: @project }) + @parameters = { + objects: [ + { + id: @group.id.to_str, + type: "Group" + } + ] + } + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @group.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status :unprocessable_entity + end + end + end + + context 'with corrupted authorization' do + before do + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/notes/'+@note.id+'/unlink', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/notes/'+@note.id+'/unlink' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_update_spec.rb b/viscoll-api/spec/requests/notes/notes_update_spec.rb new file mode 100644 index 00000000..722ea894 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_update_spec.rb @@ -0,0 +1,168 @@ +require 'rails_helper' + +describe "PUT /notes/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink"] + }) + @note = FactoryGirl.create(:note, { + type: "Ink", + project: @project, + description: "vermilion" + }) + @parameters = { + "note": { + "project_id": @project.id.to_str, + "title": "some title for note", + "type": "Ink", + "description": "sepia" + } + } + end + + context 'with valid authorization' do + context 'and valid note ID' do + before do + put '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @note.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'Updates the note' do + expect(@note.description).to eq "sepia" + end + end + + context 'and invalid note ID' do + before do + put '/notes/'+@note.id+'invalid', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context 'and failed update' do + before do + allow_any_instance_of(Note).to receive(:update).and_return(false) + put '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and out-of-context note type' do + before do + @parameters[:note][:type] = "waahoo" + put '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @note.reload + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the available options' do + expect(@body['type']).to eq 'should be one of ["Ink"]' + end + + it 'leaves the note alone' do + expect(@note.description).to eq "vermilion" + expect(@note.type).to eq "Ink" + end + end + + context "and someone else's notes" do + before do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { + user: @user2, + noteTypes: ["Ink"] + }) + @note2 = FactoryGirl.create(:note, { + type: "Ink", + project: @project2, + description: "Prussian blue" + }) + put '/notes/'+@note2.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @note2.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the note alone' do + expect(@note2.description).to eq "Prussian blue" + end + end + end + + context 'with corrupted authorization' do + before do + put '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/notes/'+@note.id, params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/notes/'+@note.id + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/notes/notes_update_type_spec.rb b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb new file mode 100644 index 00000000..71fdf4d9 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb @@ -0,0 +1,190 @@ +require 'rails_helper' + +describe "PUT /notes/type", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + noteTypes: ["Ink", "Paper"] + }) + @project.notes << FactoryGirl.create(:note, { + project_id: @project.id, + type: "Ink", + description: "Sepia" + }) + @project.notes << FactoryGirl.create(:note, { + project_id: @project.id, + type: "Paper", + description: "Parchment" + }) + @project.save + @parameters = { + "noteType": { + "project_id": @project.id.to_str, + "type": "New Paper", + "old_type": "Paper" + } + } + end + + context 'with valid authorization' do + context 'with valid parameters' do + before do + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'should return 200' do + expect(response).to have_http_status(:no_content) + end + + it 'should remove the type from the project' do + expect(@project.noteTypes).to include "Ink" + expect(@project.noteTypes).to include "New Paper" + expect(@project.noteTypes).not_to include "Paper" + end + + it 'should rename notes with that type' do + expect(@project.notes).to include an_object_having_attributes(type: "Ink") + expect(@project.notes).to include an_object_having_attributes(type: "New Paper") + expect(@project.notes).not_to include an_object_having_attributes(type: "Paper") + end + end + + context 'with missing project' do + before do + @parameters[:noteType][:project_id] += 'missing' + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['project_id']).to eq "project not found with id #{@project.id}missing" + end + end + + context 'with out-of-context type' do + before do + @parameters[:noteType][:old_type] = "Waahoo" + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['old_type']).to eq "Waahoo type doesn't exist in the project" + end + + it 'should leave the project alone' do + expect(@project.noteTypes).to eq ["Ink", "Paper"] + end + end + + context 'with duplicated target type' do + before do + @parameters[:noteType][:type] = "Ink" + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should return the right error message' do + expect(@body['type']).to eq "Ink already exists in the project" + end + + it 'should leave the project alone' do + expect(@project.noteTypes).to eq ["Ink", "Paper"] + end + end + + context 'with unauthorized project' do + before do + @user2 = FactoryGirl.create(:user) + @project.user = @user2 + @project.save + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'should return 403' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should leave the types alone' do + expect(@project.noteTypes).to eq ["Ink", "Paper"] + end + end + end + + context 'with corrupted authorization' do + before do + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/notes/type', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/notes/type' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/create_manifest_projects_spec.rb b/viscoll-api/spec/requests/projects/create_manifest_projects_spec.rb new file mode 100644 index 00000000..899ea3a2 --- /dev/null +++ b/viscoll-api/spec/requests/projects/create_manifest_projects_spec.rb @@ -0,0 +1,133 @@ +require 'rails_helper' + +describe "POST /projects/:id/manifests", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + stub_request(:get, 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/uoft_hollar.json'), headers: {}) + end + + before :each do + @parameters = { + "manifest": { + "url": "https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest" + } + } + @project = FactoryGirl.create(:project, { user: @user }) + end + + after :each do + @project.destroy + end + + context 'with valid authorization' do + context 'with valid parameters' do + before do + post "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'adds the manifest' do + expect(@project.manifests.any? { |key, manifest| manifest['url'] == "https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest"}).to be true + end + end + context 'with missing project' do + before do + post "/projects/#{@project.id.to_str}missing/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + context 'with unauthorized project' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + post "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 403' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the project alone' do + expect(@project.manifests.any? { |key, manifest| manifest['url'] == "https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest"}).to be false + end + end + context 'with exception' do + before do + allow_any_instance_of(Project).to receive(:save).and_raise("WaahooException") + post "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 400' do + expect(response).to have_http_status(:bad_request) + end + + it 'shows the exception' do + expect(@body['errors']).to eq "WaahooException" + end + end + end + + context 'with corrupted authorization' do + before do + post "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post "/projects/#{@project.id.to_str}/manifests" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/create_projects_spec.rb b/viscoll-api/spec/requests/projects/create_projects_spec.rb new file mode 100644 index 00000000..9fecbc91 --- /dev/null +++ b/viscoll-api/spec/requests/projects/create_projects_spec.rb @@ -0,0 +1,250 @@ +require 'rails_helper' + +describe "POST /projects", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @parameters = { + "project": { + "title": "Project Title" + }, + "manuscript": { + "shelfmark": "Shelfmark", + "uri": "http://www.waahoo.net", + "date": "Early 15th Century" + }, + "groups": [ + { + "number": 1, + "leaves": 4, + "conjoin": true, + "oddLeaf": 1 + }, + { + "number": 2, + "leaves": 4, + "conjoin": false, + "oddLeaf": 1 + } + ] + } + end + + context 'with correct authorization' do + context 'and standard params' do + before do + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'returns a new project' do + expect(Project.find(id: @body["projects"][0]['id'])).not_to be nil + end + end + + context 'and standard params with no groups' do + before do + @parameters.delete('groups') + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'returns a new project' do + expect(Project.find(id: @body["projects"][0]['id'])).not_to be nil + end + end + + context 'and failing params for the project' do + before do + allow_any_instance_of(Project).to receive(:save).and_return(false) + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + + context 'and non-integer leaf count' do + before do + @parameters[:groups][0][:leaves] = "ULTRAWAAHOO" + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the right leaves error' do + expect(@body['groups'][0]['leaves']).to include("should be an Integer") + end + end + + context 'and negative leaf count' do + before do + @parameters[:groups][0][:leaves] = -583 + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the right leaves error' do + expect(@body['groups'][0]['leaves']).to include("should be greater than 0") + end + end + + context 'and non-integer odd-leaf' do + before do + @parameters[:groups][0][:leaves] = 3; + @parameters[:groups][0][:oddLeaf] = "ULTRAWAAHOO" + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the right odd-leaf error' do + expect(@body['groups'][0]['oddLeaf']).to include("should be an Integer") + end + end + + context 'and negative odd-leaf' do + before do + @parameters[:groups][0][:leaves] = 3; + @parameters[:groups][0][:oddLeaf] = -2 + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the right odd-leaf error' do + expect(@body['groups'][0]['oddLeaf']).to include("should be greater than 0") + end + end + + context 'and excessive odd-leaf' do + before do + @parameters[:groups][0][:leaves] = 3; + @parameters[:groups][0][:oddLeaf] = 5 + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the right odd-leaf error' do + expect(@body['groups'][0]['oddLeaf']).to include("cannot be greater than leaves") + end + end + + context 'and non-Boolean conjoin' do + before do + @parameters[:groups][0][:conjoin] = "ULTRAWAAHOO" + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'shows the right leaves error' do + expect(@body['groups'][0]['conjoin']).to include("should be a Boolean") + end + end + + context 'and a failed create' do + before do + allow_any_instance_of(Project).to receive(:save).and_raise("Exception") + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 401' do + expect(response).to have_http_status(:bad_request) + end + + it 'includes the exception' do + expect(@body['errors']).to eq "Exception" + end + end + end + + context 'with corrupted authorization' do + before do + post '/projects', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + post '/projects', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + post '/projects', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + post '/projects' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb b/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb new file mode 100644 index 00000000..65780eff --- /dev/null +++ b/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb @@ -0,0 +1,160 @@ +require 'rails_helper' + +describe "DELETE /projects/:id/manifests", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + stub_request(:get, 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/uoft_hollar.json'), headers: {}) + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + manifests: { "59ee3c623b0eb75251207cfe": { id: "59ee3c623b0eb75251207cfe", name: 'ASDF', url: 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest', images: [{label: "IMAGE", url: "http://www.example.com/iiif-sample"}]} } + }) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project[:groupIDs] = [@defaultGroup.id.to_s] + @leaf1 = FactoryGirl.create(:leaf, {project: @project}) + @defaultGroup.add_members([@leaf1.id.to_s], 1) + @side1 = @project.sides.find(@leaf1.rectoID) + @side1.image = { label: "IMAGE", manifestID: "59ee3c623b0eb75251207cfe", url: "http://www.example.com/iiif-sample" } + @side1.save + @side2 = @project.sides.find(@leaf1.versoID) + @parameters = { + "manifest": { + "id": "59ee3c623b0eb75251207cfe" + } + } + end + + after :each do + @project.destroy + end + + context 'with valid authorization' do + context 'with valid parameters' do + before do + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'removes the manifest' do + expect(@project.manifests.any? { |key, manifest| manifest['url'] == "https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest"}).to be false + end + end + context 'with missing project' do + before do + delete "/projects/#{@project.id.to_str}missing/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + context 'with missing manifest' do + before do + @parameters[:manifest][:id] += 'missing' + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'gives the right error' do + expect(@body['error']).to eq "Manifest with id: 59ee3c623b0eb75251207cfemissing not found in project with id: #{@project.id.to_str}." + end + end + context 'with unauthorized project' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 403' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the project alone' do + expect(@project.manifests.any? { |key, manifest| manifest['url'] == "https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest"}).to be true + end + end + context 'with exception' do + before do + allow_any_instance_of(Project).to receive(:save).and_raise("WaahooException") + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 400' do + expect(response).to have_http_status(:bad_request) + end + + it 'shows the exception' do + expect(@body['errors']).to eq "WaahooException" + end + end + end + + context 'with corrupted authorization' do + before do + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete "/projects/#{@project.id.to_str}/manifests" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/destroy_projects_spec.rb b/viscoll-api/spec/requests/projects/destroy_projects_spec.rb new file mode 100644 index 00000000..5f47a313 --- /dev/null +++ b/viscoll-api/spec/requests/projects/destroy_projects_spec.rb @@ -0,0 +1,146 @@ +require 'rails_helper' + +describe "DELETE /projects/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @user2 = FactoryGirl.create(:user) + @project1 = FactoryGirl.create(:project, {:user => @user}) + @project2 = FactoryGirl.create(:project, {:user => @user}) + @project3 = FactoryGirl.create(:project, {:user => @user2}) + @deleteParameters = { + deleteUnlinkedImages: false, + } + end + + context 'with correct authorization' do + context 'and standard params' do + before do + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'returns the remaining project' do + expect(@body["projects"].length).to equal 1 + expect(@body["projects"][0]['id']).to eq @project2.id.to_str + end + + it 'leaves only the undeleted projects' do + expect(Project.where(id: @project1.id).exists?).to be false + expect(Project.where(id: @project2.id).exists?).to be true + expect(Project.where(id: @project3.id).exists?).to be true + end + end + + context 'and inexistent project' do + before do + delete '/projects/NONEXISTENT', params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'should not remove anything' do + expect(Project.where(id: @project1.id).exists?).to be true + expect(Project.where(id: @project2.id).exists?).to be true + expect(Project.where(id: @project3.id).exists?).to be true + end + end + + context "and somebody else's project" do + before do + delete '/projects/'+@project3.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not remove anything' do + expect(Project.where(id: @project1.id).exists?).to be true + expect(Project.where(id: @project2.id).exists?).to be true + expect(Project.where(id: @project3.id).exists?).to be true + end + end + + context 'and a failed delete' do + before do + allow_any_instance_of(Project).to receive(:destroy).and_raise("Exception") + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 401' do + expect(response).to have_http_status(:bad_request) + end + + it 'includes the exception' do + expect(@body['errors']).to eq "Exception" + end + end + end + + context 'with corrupted authorization' do + before do + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete '/projects/'+@project1.id + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/export_projects_spec.rb b/viscoll-api/spec/requests/projects/export_projects_spec.rb new file mode 100644 index 00000000..8e0deb40 --- /dev/null +++ b/viscoll-api/spec/requests/projects/export_projects_spec.rb @@ -0,0 +1,281 @@ +require 'rails_helper' + +describe "GET /projects/:id/export/:format", :type => :request do + before do + stub_request(:get, 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/villanova_boston.json'), headers: {}) + # Set up an account and allow sign-in + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + # Create project + @project = FactoryGirl.create(:project, + user: @user, + 'title' => 'Sample project', + 'shelfmark' => 'Ravenna 384.2339', + 'metadata' => { date: '18th century' }, + 'preferences' => { 'showTips' => true }, + 'noteTypes' => ['Ink', 'Unknown'], + 'manifests' => { '12341234': { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } } + ) + # Attach group with 2 leafs - (group with 2 leafs) - 2 conjoined leafs, 1 image + @testgroup = FactoryGirl.create(:group, project: @project, nestLevel: 1, title: 'Group 1', direction: 'left-to-right') + @upleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testgroup.id.to_s, nestLevel: 1) } + @testmidgroup = FactoryGirl.create(:group, project: @project, parentID: @testgroup.id.to_s, nestLevel: 2, title: 'Group 2', direction: 'left-to-right') + @midleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testmidgroup.id.to_s, nestLevel: 2) } + @botleafs = 2.times.collect { FactoryGirl.create(:leaf, project: @project, parentID: @testgroup.id.to_s, nestLevel: 1) } + @botleafs[1].update(type: 'Endleaf') + @project.add_groupIDs([@testgroup.id.to_s, @testmidgroup.id.to_s], 0) + @testgroup.add_members([@upleafs[0].id.to_s, @upleafs[1].id.to_s, @testmidgroup.id.to_s, @botleafs[0].id.to_s, @botleafs[1].id.to_s], 0) + @testmidgroup.add_members([@midleafs[0].id.to_s, @midleafs[1].id.to_s], 0) + @testnote = FactoryGirl.create(:note, project: @project, title: 'Test Note', type: 'Ink', description: 'This is a test', show: true, objects: {Group: [@testgroup.id.to_s], Leaf: [@botleafs[0].id.to_s], Recto: [@botleafs[0].rectoID], Verso: [@botleafs[0].versoID]}) + @testimage = FactoryGirl.create(:pixel, user: @user, projectIDs: [@project.id.to_s], sideIDs: [@upleafs[0].rectoID], filename: 'pixel.png') + Side.find(@upleafs[0].rectoID).update(image: { + manifestID: 'DIYImages', + label: "Pixel", + url: "https://dummy.library.utoronto.ca/images/#{@testimage.id}_pixel.png" + }) + end + + before :each do + @format = 'json' + end + + before :all do + imagePath = "#{Rails.root}/public/uploads" + File.new(imagePath+'/pixel', 'w') + end + + after :all do + imagePath = "#{Rails.root}/public/uploads" + if File.file?(imagePath+'/pixel') + File.delete(imagePath+'/pixel') + end + Dir.glob(imagePath+'/*.zip').each { |file| File.delete(file) } + end + + context 'with valid authorization' do + context 'for JSON export' do + before do + @format = 'json' + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 200' do + expect(response).to have_http_status(:ok) + end + + it 'should have expected content' do + export_result = @body['Export'] + image_result = @body['Images'] + expect(export_result['project']).to eq({ + 'title' => 'Sample project', + 'shelfmark' => 'Ravenna 384.2339', + 'metadata' => { 'date' => '18th century' }, + 'preferences' => { 'showTips' => true }, + 'manifests' => { '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }, + 'noteTypes' => ['Ink', 'Unknown'] + }) + expect(export_result['Groups']).to eq({ + '1' => {'params'=>{'type'=>"Quire", 'title'=>"Group 1", 'direction'=>"left-to-right", 'nestLevel'=>1}, 'tacketed'=>[], 'sewing'=>[], 'parentOrder'=>nil, 'memberOrders'=>["Leaf_1", "Leaf_2", "Group_2", "Leaf_5", "Leaf_6"]}, + '2' => {'params'=>{'type'=>"Quire", 'title'=>"Group 2", 'direction'=>"left-to-right", 'nestLevel'=>2}, 'tacketed'=>[], 'sewing'=>[], 'parentOrder'=>1, 'memberOrders'=>["Leaf_3", "Leaf_4"]} + }) + expect(export_result['Leafs']).to eq({ + '1' => {'params'=>{'material'=>"Paper", 'type'=>"Original", 'attached_above'=>"None", 'attached_below'=>"None", 'stub'=>"None", 'nestLevel'=>1}, 'conjoined_leaf_order'=>nil, 'parentOrder'=>1, 'rectoOrder'=>1, 'versoOrder'=>1}, + '2' => {'params'=>{'material'=>"Paper", 'type'=>"Original", 'attached_above'=>"None", 'attached_below'=>"None", 'stub'=>"None", 'nestLevel'=>1}, 'conjoined_leaf_order'=>nil, 'parentOrder'=>1, 'rectoOrder'=>2, 'versoOrder'=>2}, + '3' => {'params'=>{'material'=>"Paper", 'type'=>"Original", 'attached_above'=>"None", 'attached_below'=>"None", 'stub'=>"None", 'nestLevel'=>2}, 'conjoined_leaf_order'=>nil, 'parentOrder'=>2, 'rectoOrder'=>3, 'versoOrder'=>3}, + '4' => {'params'=>{'material'=>"Paper", 'type'=>"Original", 'attached_above'=>"None", 'attached_below'=>"None", 'stub'=>"None", 'nestLevel'=>2}, 'conjoined_leaf_order'=>nil, 'parentOrder'=>2, 'rectoOrder'=>4, 'versoOrder'=>4}, + '5' => {'params'=>{'material'=>"Paper", 'type'=>"Original", 'attached_above'=>"None", 'attached_below'=>"None", 'stub'=>"None", 'nestLevel'=>1}, 'conjoined_leaf_order'=>nil, 'parentOrder'=>1, 'rectoOrder'=>5, 'versoOrder'=>5}, + '6' => {'params'=>{'material'=>"Paper", 'type'=>"Endleaf", 'attached_above'=>"None", 'attached_below'=>"None", 'stub'=>"None", 'nestLevel'=>1}, 'conjoined_leaf_order'=>nil, 'parentOrder'=>1, 'rectoOrder'=>6, 'versoOrder'=>6} + }) + expect(export_result['Rectos']).to eq({ + '1' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{'manifestID' => 'DIYImages', 'label' => "Pixel", 'url' => "https://dummy.library.utoronto.ca/images/#{@testimage.id}_pixel.png"}, 'script_direction'=>"None"}, 'parentOrder'=>1}, + '2' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>2}, + '3' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>3}, + '4' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>4}, + '5' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>5}, + '6' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>6} + }) + expect(export_result['Versos']).to eq({ + '1' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>1}, + '2' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>2}, + '3' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>3}, + '4' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>4}, + '5' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>5}, + '6' => {'params'=>{'folio_number'=>"", 'page_number'=>"", 'texture'=>"None", 'image'=>{}, 'script_direction'=>"None"}, 'parentOrder'=>6} + }) + expect(export_result['Notes']).to eq({ + '1' => {'params'=>{'title'=>"Test Note", 'type'=>"Ink", 'description'=>"This is a test", 'show'=>true}, 'objects'=>{'Group'=>[1], 'Leaf'=>[5], 'Recto'=>[5], 'Verso'=>[5]}} + }) + expect(image_result['exportedImages']).to eq("https://dummy.library.utoronto.ca/api/images/zip/#{@project.id}") + end + end + + context 'for XML export' do + before do + @format = 'xml' + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 200' do + expect(response).to have_http_status(:ok) + end + + it 'should have expected content' do + expect(@body['type']).to eq 'xml' + expect(@body['Images']['exportedImages']).to eq("https://dummy.library.utoronto.ca/api/images/zip/#{@project.id}") + result = Nokogiri::XML(@body['data']) + # Metadata elements + expect(result.css("manuscript title").text).to eq 'Sample project' + expect(result.css("manuscript shelfmark").text).to eq 'Ravenna 384.2339' + expect(result.css("manuscript date").text).to eq '18th century' + expect(result.css("taxonomy[xml|id='manuscript_preferences'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['manuscript_preferences_ravenna_384_2339_showTips', 'true'] + ) + expect(result.css("taxonomy[xml|id='manifests'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['manifest_12341234', 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest'] + ) + # Quires + expect(result.css("taxonomy[xml|id='group_type'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['group_type_quire', 'Quire'] + ) + expect(result.css("taxonomy[xml|id='group_title'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['group_title_group_1', 'Group 1'], + ['group_title_group_2', 'Group 2'], + ) + expect(result.css("taxonomy[xml|id='group_members'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['group_members_ravenna_384_2339-q-1', '#ravenna_384_2339-1-1 #ravenna_384_2339-1-2 #ravenna_384_2339-q-1-2 #ravenna_384_2339-1-3 #ravenna_384_2339-1-4'], + ['group_members_ravenna_384_2339-q-1-2', '#ravenna_384_2339-1-2-3 #ravenna_384_2339-1-2-4'], + ) + # Leaves + expect(result.css("taxonomy[xml|id='leaf_material'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['leaf_material_paper', 'Paper'] + ) + expect(result.css("manuscript leaf").collect { |t| [t['xml:id'], t.css('folioNumber').first.text, t.css('q').first['target'], t.css('q').first['n']] }).to include( + ['ravenna_384_2339-1-1', '1', '#ravenna_384_2339-q-1', '1'], + ['ravenna_384_2339-1-2', '2', '#ravenna_384_2339-q-1', '1'], + ['ravenna_384_2339-1-2-3', '3', '#ravenna_384_2339-q-1-2', '2'], + ['ravenna_384_2339-1-2-4', '4', '#ravenna_384_2339-q-1-2', '2'], + ['ravenna_384_2339-1-3', '5', '#ravenna_384_2339-q-1', '1'], + ['ravenna_384_2339-1-4', '6', '#ravenna_384_2339-q-1', '1'] + ) + # Sides and Notes + + expect(result.css("mapping map").collect { |t| [t['target'], t['side'], t.css('term').first['target']]}).to include( + ['#ravenna_384_2339-1-1', 'recto', '#side_page_number_EMPTY https://dummy.library.utoronto.ca/images/'+@testimage.id.to_s+'_pixel.png #manifest_DIYImages'], + ['#ravenna_384_2339-1-2', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-3', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-4', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-3', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-4', 'recto', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-1', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-3', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-2-4', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-3', 'verso', '#side_page_number_EMPTY'], + ['#ravenna_384_2339-1-4', 'verso', '#side_page_number_EMPTY'] + ) + expect(result.css("mapping map").collect { |t| [t['target'], t.css('term').first['target']]}).to include( + ['#ravenna_384_2339-n-1', '#note_title_test_note #note_show'], + ) + end + end + + context 'with missing project' do + before do + get "/projects/#{@project.id}missing/export/#{@format}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 404' do + expect(response).to have_http_status(:not_found) + end + + it 'should show error' do + expect(@body['error']).to eq "project not found with id #{@project.id}missing" + end + end + + context 'with unauthorized project' do + before do + @project.update(user: FactoryGirl.create(:user)) + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'should return 401' do + expect(response).to have_http_status(:unauthorized) + end + end + + context 'with invalid format' do + before do + @format = 'waahoo' + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'should return 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'should show error' do + expect(@body['error']).to eq "Export format must be one of [json, xml]" + end + end + end + + context 'with corrupted authorization' do + before do + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + get "/projects/#{@project.id}/export/#{@format}", headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/projects/import' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/filter_projects_spec.rb b/viscoll-api/spec/requests/projects/filter_projects_spec.rb new file mode 100644 index 00000000..e7ed6948 --- /dev/null +++ b/viscoll-api/spec/requests/projects/filter_projects_spec.rb @@ -0,0 +1,1018 @@ +require 'rails_helper' + +describe "PUT /projects/:id/filter", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + @user2 = FactoryGirl.create(:user, {:password => "user2"}) + @project1 = FactoryGirl.create(:codex_project, :user => @user, :quire_structure => [[4, 6]]) + @project2 = FactoryGirl.create(:codex_project, :user => @user2, :quire_structure => [[4, 6]]) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @parameters = { + "queries": [ + { + } + ] + } + end + + it 'should be sane' do + expect(@project1.groups.count).to eq 4 + expect(@project1.groups.collect { |g| g.id }.count).to eq 4 + end + + context 'with correct authorization' do + context 'and group-based queries' do + context 'equals one' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "equals", + "values": [ @project1.groups[0].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project2.groups[0].id.to_s) + end + end + + context 'equals multiple' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "equals", + "values": [ @project1.groups[0].title, @project2.groups[0].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project2.groups[0].id.to_s) + end + end + + context 'contains one' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ @project1.groups[0].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project2.groups[0].id.to_s) + end + end + + context 'contains multiple' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ @project1.groups[0].title, @project2.groups[0].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project2.groups[0].id.to_s) + end + end + + context 'not equals one' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "not equals", + "values": [ @project1.groups[0].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).not_to include(@project1.groups[0].id.to_s) + @project1.groups[1..-1].each do |should_have_group| + expect(body['Groups']).to include(should_have_group.id.to_s) + end + end + end + + context 'not equals multiple' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "not equals", + "values": [ @project1.groups[0].title, @project1.groups[1].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).not_to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project1.groups[1].id.to_s) + @project1.groups[2..-1].each do |should_have_group| + expect(body['Groups']).to include(should_have_group.id.to_s) + end + end + end + + context 'not contains one' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "not contains", + "values": [ @project1.groups[0].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).not_to include(@project1.groups[0].id.to_s) + @project1.groups[1..-1].each do |should_have_group| + expect(body['Groups']).to include(should_have_group.id.to_s) + end + end + end + + context 'not contains multiple' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "not contains", + "values": [ @project1.groups[0].title, @project1.groups[1].title ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).not_to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project1.groups[1].id.to_s) + @project1.groups[2..-1].each do |should_have_group| + expect(body['Groups']).to include(should_have_group.id.to_s) + end + end + end + end + + context 'and leaf-based queries' do + context 'equals one' do + before do + @project1.leafs[5].update(material: 'Copy paper') + @project2.leafs[5].update(material: 'Copy paper') + @parameters = { + "queries": [ + { + "type": "leaf", + "attribute": "material", + "condition": "equals", + "values": [ 'Copy paper' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Leafs']).to eq [@project1.leafs[5].id.to_s] + end + end + + context 'equals multiple' do + before do + @project1.leafs[5].update(material: 'Copy paper') + @project1.leafs[13].update(material: 'Copy paper') + @project1.leafs[16].update(material: 'Plastic') + @project2.leafs[5].update(material: 'Copy paper') + @project2.leafs[13].update(material: 'Copy paper') + @project2.leafs[16].update(material: 'Plastic') + @parameters = { + "queries": [ + { + "type": "leaf", + "attribute": "material", + "condition": "equals", + "values": [ 'Copy paper', 'Plastic' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Leafs'].length).to eq 3 + expect(body['Leafs']).to include(@project1.leafs[5].id.to_s) + expect(body['Leafs']).to include(@project1.leafs[13].id.to_s) + expect(body['Leafs']).to include(@project1.leafs[16].id.to_s) + end + end + + context 'not equals one' do + before do + @project1.leafs[5].update(material: 'Copy paper') + @project2.leafs[5].update(material: 'Copy paper') + @parameters = { + "queries": [ + { + "type": "leaf", + "attribute": "material", + "condition": "not equals", + "values": [ 'Copy paper' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Leafs'].count).to eq @project1.leafs.count-1 + expect(body['Leafs']).not_to include(@project1.leafs[5].id.to_s) + expect(body['Leafs']).not_to include(@project2.leafs[5].id.to_s) + end + end + + context 'not equals multiple' do + before do + @project1.leafs[5].update(material: 'Copy paper') + @project1.leafs[13].update(material: 'Copy paper') + @project1.leafs[16].update(material: 'Plastic') + @project2.leafs[5].update(material: 'Copy paper') + @project2.leafs[13].update(material: 'Copy paper') + @project2.leafs[16].update(material: 'Plastic') + @parameters = { + "queries": [ + { + "type": "leaf", + "attribute": "material", + "condition": "not equals", + "values": [ 'Copy paper', 'Plastic' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Leafs'].count).to eq @project1.leafs.count-3 + expect(body['Leafs']).not_to include(@project1.leafs[5].id.to_s) + expect(body['Leafs']).not_to include(@project1.leafs[13].id.to_s) + expect(body['Leafs']).not_to include(@project1.leafs[16].id.to_s) + expect(body['Leafs']).not_to include(@project2.leafs[5].id.to_s) + expect(body['Leafs']).not_to include(@project2.leafs[13].id.to_s) + expect(body['Leafs']).not_to include(@project2.leafs[16].id.to_s) + end + end + + context 'with legacy conjoined_leaf_order attribute' do + before do + @parameters = { + "queries": [ + { + "type": "leaf", + "attribute": "conjoined_leaf_order", + "condition": "equals", + "values": [ @project1.leafs[-1].conjoined_to ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Leafs']).to eq [@project1.leafs[-1].id.to_s] + end + end + end + + context 'and side-based queries' do + context 'equals one' do + before do + @project1.sides[7].update(script_direction: 'Top-To-Bottom') + @project2.sides[7].update(script_direction: 'Top-To-Bottom') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "script_direction", + "condition": "equals", + "values": [ 'Top-To-Bottom' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides']).to eq [@project1.sides[7].id.to_s] + end + end + + context 'equals multiple' do + before do + @project1.sides[7].update(script_direction: 'Top-To-Bottom') + @project1.sides[10].update(script_direction: 'Left-To-Right') + @project2.sides[7].update(script_direction: 'Top-To-Bottom') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "script_direction", + "condition": "equals", + "values": [ 'Top-To-Bottom', 'Left-To-Right' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides'].count).to eq 2 + expect(body['Sides']).to include @project1.sides[7].id.to_s + expect(body['Sides']).to include @project1.sides[10].id.to_s + end + end + + context 'not equals one' do + before do + @project1.sides[7].update(script_direction: 'Top-To-Bottom') + @project2.sides[7].update(script_direction: 'Top-To-Bottom') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "script_direction", + "condition": "not equals", + "values": [ 'Top-To-Bottom' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides'].count).to eq @project1.sides.count-1 + expect(body['Sides']).not_to include @project1.sides[7].id.to_s + end + end + + context 'not equals multiple' do + before do + @project1.sides[7].update(script_direction: 'Top-To-Bottom') + @project1.sides[10].update(script_direction: 'Left-To-Right') + @project2.sides[7].update(script_direction: 'Top-To-Bottom') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "script_direction", + "condition": "not equals", + "values": [ 'Top-To-Bottom', 'Left-To-Right' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides'].count).to eq @project1.sides.count-2 + expect(body['Sides']).not_to include @project1.sides[7].id.to_s + expect(body['Sides']).not_to include @project1.sides[10].id.to_s + end + end + + context 'contains one' do + before do + @project1.sides[9].update(folio_number: 'FN0') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "folio_number", + "condition": "contains", + "values": [ 'FN' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides']).to eq [@project1.sides[9].id.to_s] + end + end + + context 'contains multiple' do + before do + @project1.sides[6].update(folio_number: 'FN0') + @project1.sides[11].update(folio_number: 'QR1') + @project2.sides[7].update(folio_number: 'FN0') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "folio_number", + "condition": "contains", + "values": [ 'FN', 'QR' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides'].count).to eq 2 + expect(body['Sides']).to include @project1.sides[6].id.to_s + expect(body['Sides']).to include @project1.sides[11].id.to_s + end + end + + context 'not contains one' do + before do + @project1.sides[9].update(folio_number: 'FN0') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "folio_number", + "condition": "not contains", + "values": [ 'FN' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides'].count).to eq @project1.sides.count-1 + expect(body['Sides']).not_to include @project1.sides[9].id.to_s + end + end + + context 'not contains multiple' do + before do + @project1.sides[6].update(folio_number: 'FN0') + @project1.sides[11].update(folio_number: 'QR1') + @project2.sides[7].update(folio_number: 'FN0') + @parameters = { + "queries": [ + { + "type": "side", + "attribute": "folio_number", + "condition": "not contains", + "values": [ 'FN', 'QR' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Sides'].count).to eq @project1.sides.count-2 + expect(body['Sides']).not_to include @project1.sides[6].id.to_s + expect(body['Sides']).not_to include @project1.sides[11].id.to_s + expect(body['Sides']).not_to include @project2.sides[7].id.to_s + end + end + end + + context 'and note-based queries' do + before do + @note1 = FactoryGirl.create(:note, project_id: @project1.id, attachments: [@project1.groups[1], @project1.leafs[5], @project1.sides[14], @project1.sides[15]], title: "ULTRA WAAHOO") + @note2 = FactoryGirl.create(:note, project_id: @project1.id, attachments: [@project1.groups[2], @project1.leafs[7], @project1.sides[2], @project1.sides[3]], title: "XTREME FOOBAR") + @note3 = FactoryGirl.create(:note, project_id: @project1.id, attachments: [@project1.groups[3], @project1.leafs[3], @project1.sides[10], @project1.sides[11]], title: "CREEPY WAAHOO") + @notebad = FactoryGirl.create(:note, project_id: @project2.id, attachments: [@project2.groups[1], @project2.leafs[5], @project2.sides[14], @project2.sides[15]], title: "ULTRA WAAHOO") + end + + context "equals one" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "equals", + "values": [ 'ULTRA WAAHOO' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes']).to eq [@note1.id.to_s] + end + end + + context "equals multiple" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "equals", + "values": [ 'CREEPY WAAHOO', 'XTREME FOOBAR' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes'].count).to eq 2 + expect(body['Notes']).to include @note2.id.to_s + expect(body['Notes']).to include @note3.id.to_s + end + end + + context "not equals one" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "not equals", + "values": [ 'ULTRA WAAHOO' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes'].count).to eq @project1.notes.count-1 + expect(body['Notes']).not_to include @note1.id.to_s + expect(body['Notes']).not_to include @notebad.id.to_s + end + end + + context "not equals multiple" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "not equals", + "values": [ 'ULTRA WAAHOO', 'CREEPY WAAHOO' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes'].count).to eq @project1.notes.count-2 + expect(body['Notes']).not_to include @note1.id.to_s + expect(body['Notes']).not_to include @note3.id.to_s + expect(body['Notes']).not_to include @notebad.id.to_s + end + end + + context "contains one" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "contains", + "values": [ 'ULTRA' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes']).to eq [@note1.id.to_s] + end + end + + context "contains multiple" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "contains", + "values": [ 'CREEPY', 'XTREME' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes'].count).to eq 2 + expect(body['Notes']).to include @note2.id.to_s + expect(body['Notes']).to include @note3.id.to_s + end + end + + context "not contains one" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "not contains", + "values": [ 'ULTRA' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes'].count).to eq @project1.notes.count-1 + expect(body['Notes']).not_to include @note1.id.to_s + end + end + + context "not contains multiple" do + before do + @parameters = { + "queries": [ + { + "type": "note", + "attribute": "title", + "condition": "not contains", + "values": [ 'CREEPY', 'XTREME' ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Notes'].count).to eq @project1.notes.count-2 + expect(body['Notes']).not_to include @note2.id.to_s + expect(body['Notes']).not_to include @note3.id.to_s + end + end + end + + context 'and compound conditions' do + context 'using AND' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ 'Quire' ], + "conjunction": "AND" + }, + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ @project1.groups[0].title[5..-1] ] + } + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups']).to include(@project1.groups[0].id.to_s) + expect(body['Groups']).not_to include(@project2.groups[0].id.to_s) + end + end + + context 'using OR' do + before do + @parameters = { + "queries": [ + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ 'Quire' ], + "conjunction": "OR" + }, + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ 'asdf' ], + "conjunction": "OR" + }, + { + "type": "group", + "attribute": "title", + "condition": "contains", + "values": [ '4' ] + }, + ] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'contains the expected entries' do + body = JSON.parse(response.body) + expect(body['Groups'].count).to eq @project1.groups.count + @project1.groups.each do |grp| + expect(body['Groups']).to include grp.id.to_s + end + end + end + end + + context 'and inexistent params' do + before do + @parameters = { + "queries": [] + } + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and missing project' do + before do + put "/projects/#{@project1.id}missing/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context 'and unauthorized params' do + before do + put "/projects/#{@project2.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + end + end + + context 'with corrupted authorization' do + before do + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => @authToken+"invalid"} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put "/projects/#{@project1.id}/filter", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put "/projects/#{@project1.id}/filter" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/import_projects_spec.rb b/viscoll-api/spec/requests/projects/import_projects_spec.rb new file mode 100644 index 00000000..45be3d96 --- /dev/null +++ b/viscoll-api/spec/requests/projects/import_projects_spec.rb @@ -0,0 +1,143 @@ +require 'rails_helper' + +describe "PUT /projects/import", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @parameters = { + "importData": nil, + "importFormat": nil, + "imageData": nil + } + end + + describe 'JSON imports' do + let(:import_data) { File.open(File.dirname(__FILE__) + '/../../fixtures/sample_import_json.json', 'r') { |file| file.read } } + before :each do + @parameters[:importFormat] = 'json' + end + + it 'should import properly' do + @parameters[:importData] = import_data + expect{ put '/projects/import', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} }.to change{Project.count}.by(1) + expect(response).to have_http_status(:ok) + project = Project.last + expect(project.title).to eq 'Sample project' + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to eq ['Hand', 'Ink', 'Unknown'] + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', 'name' => 'Boston, and Bunker Hill.' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + + it 'should show error for invalid JSON' do + @parameters[:importData] = import_data + '{}[];;' + expect{ put '/projects/import', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} }.not_to change{Project.count} + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['error']).to eq("Sorry, the imported data cannot be validated. Please check your file for errors and make sure the correct import format is selected above.") + end + end + + describe 'XML imports' do + let(:import_data) { File.open(File.dirname(__FILE__) + '/../../fixtures/sample_import_xml.xml', 'r') { |file| file.read } } + before :each do + @parameters[:importFormat] = 'xml' + end + + it 'should import properly' do + @parameters[:importData] = import_data + expect{ put '/projects/import', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} }.to change{Project.count}.by(1) + project = Project.last + expect(project.title).to eq 'Sample project' + expect(project.shelfmark).to eq 'Ravenna 384.2339' + expect(project.metadata).to eq({ 'date' => '18th century' }) + expect(project.preferences).to eq({ 'showTips' => true }) + expect(project.noteTypes).to include('Ink', 'Unknown') + expect(project.manifests).to eq({ '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest' } }) + expect(project.leafs.count).to eq 6 + expect(project.sides.count).to eq 12 + expect(project.notes[0].title).to eq 'Test Note' + expect(project.notes[0].type).to eq 'Ink' + expect(project.notes[0].description).to eq 'This is a test' + expect(project.notes[0].objects).to eq({'Group' => [project.groups[0].id.to_s], 'Leaf' => [project.leafs[4].id.to_s], 'Recto' => [project.leafs[4].rectoID], 'Verso' => [project.leafs[4].versoID]}) + end + + it 'should show error for invalid XML' do + @parameters[:import_data] = import_data + ' @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} }.not_to change{Project.count} + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['error']).not_to be_blank + end + end + + describe 'Invalid situations' do + let(:import_data) { File.open(File.dirname(__FILE__) + '/../../fixtures/sample_import_json.json', 'r') { |file| file.read } } + before :each do + @parameters[:importFormat] = 'json' + end + + context 'with corrupted authorization' do + before do + put '/projects/import', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/projects/import', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/projects/import', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/projects/import' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end + end +end diff --git a/viscoll-api/spec/requests/projects/index_projects_spec.rb b/viscoll-api/spec/requests/projects/index_projects_spec.rb new file mode 100644 index 00000000..0e7410fe --- /dev/null +++ b/viscoll-api/spec/requests/projects/index_projects_spec.rb @@ -0,0 +1,86 @@ +require 'rails_helper' + +describe "GET /projects", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + context 'with correct authorization' do + context 'and standard params' do + before do + @user2 = FactoryGirl.create(:user) + @project1 = FactoryGirl.create(:project, {:user_id => @user.id}) + @project2 = FactoryGirl.create(:project, {:user_id => @user.id}) + @project3 = FactoryGirl.create(:project, {:user_id => @user2.id}) + get '/projects', params: '', headers: {'Authorization' => @authToken} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it "contains the user's own projects only" do + expect(@body["projects"].length).to eq 2 + expect(@body["projects"][0]['id']).to eq @project2.id.to_str + expect(@body["projects"][1]['id']).to eq @project1.id.to_str + end + end + end + + context 'with corrupted authorization' do + before do + get '/projects', params: '', headers: {'Authorization' => @authToken+"invalid"} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + get '/projects', params: '', headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + get '/projects', params: '', headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + get '/projects' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/show_projects_spec.rb b/viscoll-api/spec/requests/projects/show_projects_spec.rb new file mode 100644 index 00000000..44a32c56 --- /dev/null +++ b/viscoll-api/spec/requests/projects/show_projects_spec.rb @@ -0,0 +1,103 @@ +require 'rails_helper' + +describe "GET /projects/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + @user2 = FactoryGirl.create(:user, {:password => "user2"}) + @project1 = FactoryGirl.create(:project, {:user => @user}) + @project2 = FactoryGirl.create(:project, {:user => @user2}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + context 'with correct authorization' do + context 'and standard params' do + before do + get '/projects/'+@project1.id, params: '', headers: {'Authorization' => @authToken} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it "contains the user's own projects only" do + expect(@body['active']['id']).to eq @project1.id.to_str + end + end + + context 'and inexistent params' do + before do + get '/projects/ULTRAWAAHOO', params: '', headers: {'Authorization' => @authToken} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context 'and unauthorized params' do + before do + get '/projects/'+@project2._id, params: '', headers: {'Authorization' => @authToken} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + end + end + + context 'with corrupted authorization' do + before do + get '/projects/'+@project1.id, params: '', headers: {'Authorization' => @authToken+"invalid"} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + get '/projects/'+@project1.id, params: '', headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + get '/projects/'+@project1.id, params: '', headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + get '/projects/'+@project1.id + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb b/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb new file mode 100644 index 00000000..ab2fed76 --- /dev/null +++ b/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb @@ -0,0 +1,179 @@ +require 'rails_helper' + +describe "PUT /projects/:id/manifests", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + stub_request(:get, 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest').with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' }).to_return(status: 200, body: File.read(File.dirname(__FILE__) + '/../../fixtures/uoft_hollar.json'), headers: {}) + end + + before :each do + @project = FactoryGirl.create(:project, { + user: @user, + manifests: { "59ee3c623b0eb75251207cfe": { id: "59ee3c623b0eb75251207cfe", name: 'ASDF', url: 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest', images: [{label: "IMAGE", url: "http://www.example.com/iiif-sample"}]} } + }) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project[:groupIDs] = [@defaultGroup.id.to_s] + @leaf1 = FactoryGirl.create(:leaf, {project: @project}) + @defaultGroup.add_members([@leaf1.id.to_s], 1) + @side1 = @project.sides.find(@leaf1.rectoID) + @side1.image = { label: "IMAGE", manifestID: "59ee3c623b0eb75251207cfe", url: "http://www.example.com/iiif-sample" } + @side1.save + @side2 = @project.sides.find(@leaf1.versoID) + @parameters = { + "manifest": { + "id": "59ee3c623b0eb75251207cfe", + "name": "QWER", + "url": 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest' + } + } + end + + after :each do + @project.destroy + end + + context 'with valid authorization' do + context 'with valid parameters' do + before do + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'rename the manifest' do + expect(@project.manifests.any? { |key, manifest| manifest['name'] == "ASDF"}).to be false + expect(@project.manifests.any? { |key, manifest| manifest['name'] == "QWER"}).to be true + end + end + context 'with missing project' do + before do + put "/projects/#{@project.id.to_str}missing/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + context 'with missing manifest' do + before do + @parameters[:manifest][:id] += 'missing' + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'gives the right error' do + expect(@body['error']).to eq "Manifest with id: 59ee3c623b0eb75251207cfemissing not found in project with id: #{@project.id.to_str}." + end + end + context 'with missing id parameter' do + before do + @parameters[:manifest].delete(:id) + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'gives the right error' do + expect(@body['error']).to eq "Param required: id." + end + end + context 'with unauthorized project' do + before do + @project.user = FactoryGirl.create(:user) + @project.save + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @project.reload + end + + it 'returns 403' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the project alone' do + expect(@project.manifests.any? { |key, manifest| manifest['name'] == "ASDF"}).to be true + expect(@project.manifests.any? { |key, manifest| manifest['name'] == "QWER"}).to be false + end + end + context 'with exception' do + before do + allow_any_instance_of(Project).to receive(:save).and_raise("WaahooException") + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 400' do + expect(response).to have_http_status(:bad_request) + end + + it 'shows the exception' do + expect(@body['errors']).to eq "WaahooException" + end + end + end + + context 'with corrupted authorization' do + before do + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put "/projects/#{@project.id.to_str}/manifests", params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put "/projects/#{@project.id.to_str}/manifests" + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/projects/update_projects_spec.rb b/viscoll-api/spec/requests/projects/update_projects_spec.rb new file mode 100644 index 00000000..bc864c49 --- /dev/null +++ b/viscoll-api/spec/requests/projects/update_projects_spec.rb @@ -0,0 +1,174 @@ +require 'rails_helper' + +describe "PUT /projects/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @user2 = FactoryGirl.create(:user) + @project1 = FactoryGirl.create(:project, {:user => @user}) + @project2 = FactoryGirl.create(:project, {:user => @user}) + @project3 = FactoryGirl.create(:project, {:user => @user2}) + @parameters = { + "project": { + "title": "My modified project", + "shelfmark": "MSS 123", + "metadata": { + "date": "18th century" + }, + "manifests": [ + {"name": "barrenlands", "url": "https://iiif.library.utoronto.ca/presentation/v2/barrenlands:C10034/manifest"}, + {"name": "insulin", "url": "https://iiif.library.utoronto.ca/presentation/v2/insulin:E10016/manifest"} + ], + "noteTypes": [ + "Ink", + "Hand" + ], + "preferences": { + "showTips": false + } + } + } + end + + context 'with correct authorization' do + context 'and standard params' do + before do + put '/projects/'+@project1.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'returns the changed project' do + expect(@body["projects"][0]['id']).to eq @project1.id.to_str + end + + it 'changes the right project' do + expect(Project.find(id: @project1.id).title).to eq "My modified project" + expect(Project.find(id: @project2.id).title).not_to eq "My modified project" + expect(Project.find(id: @project3.id).title).not_to eq "My modified project" + end + end + + context 'and inexistent project' do + before do + put '/projects/NONEXISTENT', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + + it 'should not remove anything' do + expect(Project.find(id: @project1.id).title).not_to eq "My modified project" + expect(Project.find(id: @project2.id).title).not_to eq "My modified project" + expect(Project.find(id: @project3.id).title).not_to eq "My modified project" + end + end + + context "and somebody else's project" do + before do + put '/projects/'+@project3.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'should not remove anything' do + expect(Project.find(id: @project1.id).title).not_to eq "My modified project" + expect(Project.find(id: @project2.id).title).not_to eq "My modified project" + expect(Project.find(id: @project3.id).title).not_to eq "My modified project" + end + end + + context 'and a failed save' do + before do + allow_any_instance_of(Project).to receive(:update).and_return(false) + put '/projects/'+@project1.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'and an exception' do + before do + allow_any_instance_of(Project).to receive(:update).and_raise("Exception") + put '/projects/'+@project1.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns 401' do + expect(response).to have_http_status(:bad_request) + end + + it 'includes the exception' do + expect(@body['errors']).to eq "Exception" + end + end + end + + context 'with corrupted authorization' do + before do + put '/projects/'+@project1.id, params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/projects/'+@project1.id, params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/projects/'+@project1.id, params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/projects/'+@project1.id + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/sides/sides_generateFolio_spec.rb b/viscoll-api/spec/requests/sides/sides_generateFolio_spec.rb new file mode 100644 index 00000000..fa2f8826 --- /dev/null +++ b/viscoll-api/spec/requests/sides/sides_generateFolio_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +describe "PUT /sides/generateFolio", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user}) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@defaultGroup.id.to_s], 0) + @leaf1 = FactoryGirl.create(:leaf, {project: @project}) + @leaf2 = FactoryGirl.create(:leaf, {project: @project}) + @defaultGroup.add_members([@leaf1.id.to_s, @leaf2.id.to_s], 1) + @parameters = { + rectoIDs: [@leaf1.rectoID, @leaf2.rectoID], + versoIDs: [@leaf1.versoID, @leaf2.versoID], + startNumber: 9, + } + end + + context 'generate folio number' do + before do + put '/sides/generateFolio', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'Updates the side folio numbers' do + side1R = @project.sides.find(@leaf1.rectoID) + side1V = @project.sides.find(@leaf1.versoID) + side2R = @project.sides.find(@leaf2.rectoID) + side2V = @project.sides.find(@leaf2.versoID) + expect(side1R.folio_number).to eq "9R" + expect(side1V.folio_number).to eq "9V" + expect(side2R.folio_number).to eq "10R" + expect(side2V.folio_number).to eq "10V" + end + end +end \ No newline at end of file diff --git a/viscoll-api/spec/requests/sides/sides_generatePage_spec.rb b/viscoll-api/spec/requests/sides/sides_generatePage_spec.rb new file mode 100644 index 00000000..9bfc0937 --- /dev/null +++ b/viscoll-api/spec/requests/sides/sides_generatePage_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +describe "PUT /sides/generatePageNumber", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user}) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@defaultGroup.id.to_s], 0) + @leaf1 = FactoryGirl.create(:leaf, {project: @project}) + @leaf2 = FactoryGirl.create(:leaf, {project: @project}) + @defaultGroup.add_members([@leaf1.id.to_s, @leaf2.id.to_s], 1) + @parameters = { + rectoIDs: [@leaf1.rectoID, @leaf2.rectoID], + versoIDs: [@leaf1.versoID, @leaf2.versoID], + startNumber: 3, + } + end + + context 'generate page number' do + before do + put '/sides/generatePageNumber', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'Updates the side page numbers' do + side1R = @project.sides.find(@leaf1.rectoID) + side1V = @project.sides.find(@leaf1.versoID) + side2R = @project.sides.find(@leaf2.rectoID) + side2V = @project.sides.find(@leaf2.versoID) + expect(side1R.page_number).to eq "3" + expect(side1V.page_number).to eq "4" + expect(side2R.page_number).to eq "5" + expect(side2V.page_number).to eq "6" + end + end +end \ No newline at end of file diff --git a/viscoll-api/spec/requests/sides/sides_updateMultiple_spec.rb b/viscoll-api/spec/requests/sides/sides_updateMultiple_spec.rb new file mode 100644 index 00000000..9b7efc63 --- /dev/null +++ b/viscoll-api/spec/requests/sides/sides_updateMultiple_spec.rb @@ -0,0 +1,163 @@ +require 'rails_helper' + +describe "PUT /sides/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user}) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@defaultGroup.id.to_s], 0) + @leaf1 = FactoryGirl.create(:leaf, {project: @project}) + @defaultGroup.add_members([@leaf1.id.to_s], 1) + @side1 = @project.sides.find(@leaf1.rectoID) + @side2 = @project.sides.find(@leaf1.versoID) + @parameters = { + "sides": [ + { + "id": @side1.id.to_str, + "attributes": { + "texture": "PaperSide1", + "script_direction": "LeftSide1" + } + }, + { + "id": @side2.id.to_str, + "attributes": { + "texture": "PaperSide2", + "script_direction": "LeftSide2" + } + } + ] + } + end + + context 'with valid authorization' do + context 'and valid side ID' do + before do + put '/sides', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side1.reload + @side2.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'Updates the sides' do + expect(@side1.texture).to eq "PaperSide1" + expect(@side1.script_direction).to eq "LeftSide1" + expect(@side2.texture).to eq "PaperSide2" + expect(@side2.script_direction).to eq "LeftSide2" + end + end + + context 'and invalid side ID' do + before do + @parameters[:sides][0][:id] = "invalidID" + put '/sides', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context "and someone else's sides" do + before do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { user: @user2 }) + @defaultGroup2 = FactoryGirl.create(:quire, project: @project) + @leaf2 = FactoryGirl.create(:leaf, {project: @project2}) + @defaultGroup.add_members([@leaf2.id.to_s], 1) + @side3 = @project2.sides.find(@leaf2.rectoID) + @side4 = @project2.sides.find(@leaf2.versoID) + @parameters = { + "sides": [ + { + "id": @side3.id, + "attributes": { + "texture": "PaperSide1", + "script_direction": "LeftSide1" + } + }, + { + "id": @side4.id, + "attributes": { + "texture": "PaperSide2", + "script_direction": "LeftSide2" + } + } + ] + } + put '/sides', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side1.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the side alone' do + expect(@side3.texture).to eq "None" + end + end + end + + context 'with corrupted authorization' do + before do + put '/sides', params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/sides', params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/sides', params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/sides' + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/sides/sides_update_spec.rb b/viscoll-api/spec/requests/sides/sides_update_spec.rb new file mode 100644 index 00000000..1d79af59 --- /dev/null +++ b/viscoll-api/spec/requests/sides/sides_update_spec.rb @@ -0,0 +1,147 @@ +require 'rails_helper' + +describe "PUT /sides/id", :type => :request do + before do + @user = FactoryGirl.create(:user, {:password => "user"}) + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email => @user.email, :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + before :each do + @project = FactoryGirl.create(:project, {user: @user}) + @defaultGroup = FactoryGirl.create(:quire, project: @project) + @project.add_groupIDs([@defaultGroup.id.to_s], 0) + @leaf1 = FactoryGirl.create(:leaf, {project: @project}) + @defaultGroup.add_members([@leaf1.id.to_s], 1) + @side1 = @project.sides.find(@leaf1.rectoID) + @side2 = @project.sides.find(@leaf1.versoID) + @parameters = { + "side": { + "folio_number": "some folio_number for side", + "texture": "Paper", + "script_direction": "Left", + "image": { + "manifestID": "123", + "url": "https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3002_0001", + "label": "3002_0001" + } + } + } + end + + context 'with valid authorization' do + context 'and valid side ID' do + before do + put '/sides/'+@side1.id.to_s, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side1.reload + end + + it 'returns 204' do + expect(response).to have_http_status(:no_content) + end + + it 'Updates the side' do + expect(@side1.texture).to eq "Paper" + expect(@side1.script_direction).to eq "Left" + expect(@side1.image[:url]).to eq "https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3002_0001" + end + end + + context 'and invalid side ID' do + before do + put '/sides/'+@side1.id.to_s+'invalid', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 404' do + expect(response).to have_http_status(:not_found) + end + end + + context 'and failed update' do + before do + allow_any_instance_of(Side).to receive(:update).and_return(false) + put '/sides/'+@side1.id.to_s, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + end + + it 'returns 422' do + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context "and someone else's sides" do + before do + @user2 = FactoryGirl.create(:user) + @project2 = FactoryGirl.create(:project, { user: @user2 }) + @defaultGroup2 = FactoryGirl.create(:quire, project: @project) + @leaf2 = FactoryGirl.create(:leaf, {project: @project2}) + @defaultGroup.add_members([@leaf2.id.to_s], 1) + @side3 = @project2.sides.find(@leaf2.rectoID) + @side4 = @project2.sides.find(@leaf2.versoID) + put '/sides/'+@side3.id, params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @side1.reload + end + + it 'returns 401' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the side alone' do + expect(@side3.texture).to eq "None" + end + end + end + + context 'with corrupted authorization' do + before do + put '/sides/'+@side1.id, params: @parameters.to_json, headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + @body = JSON.parse(response.body) + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/sides/'+@side1.id, params: @parameters.to_json, headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/sides/'+@side1.id, params: @parameters.to_json, headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/sides/'+@side1.id + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/users/delete_users_userID_spec.rb b/viscoll-api/spec/requests/users/delete_users_userID_spec.rb new file mode 100644 index 00000000..9c0fb43e --- /dev/null +++ b/viscoll-api/spec/requests/users/delete_users_userID_spec.rb @@ -0,0 +1,120 @@ +require 'rails_helper' + +describe "DELETE /users/userID", :type => :request do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + context 'with correct authorization' do + context 'and valid params' do + before do + @user2 = User.create(:name => "user2", :email => "user2@mail.com", :password => "user2") + @project1 = Project.create(:title => "first project", :user_id => @user.id) + @project2 = Project.create(:title => "second project", :user_id => @user.id) + @project3 = Project.create(:title => "some other user project", :user_id => @user2.id) + delete '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => @authToken} + end + + it 'returns a successful no_content response' do + expect(response).to have_http_status(:no_content) + end + + it 'deletes the user from the database' do + expect(User.where(id: @user.id).count).to eq(0) + end + + it 'deletes all user related objects only' do + expect(Project.where(id: @project1.id).count).to eq(0) + expect(Project.where(id: @project2.id).count).to eq(0) + expect(Project.all.count).to eq(1) + end + end + + context 'and another user' do + before do + @user2 = User.create(:name => "user2", :email => "user2@mail.com", :password => "user2") + @project1 = Project.create(:title => "first project", :user_id => @user.id) + @project2 = Project.create(:title => "second project", :user_id => @user.id) + @project3 = Project.create(:title => "some other user project", :user_id => @user2.id) + delete '/users/'+@user2.id.to_s, params: '', headers: {'Authorization' => @authToken} + end + + it 'returns unauthorized' do + expect(response).to have_http_status(:unauthorized) + end + + it 'leaves the other user alone' do + expect(User.where(id: @user2.id).count).to eq 1 + end + end + + context 'and invalid params' do + before do + delete '/users/invalidID', params: '', headers: {'Authorization' => @authToken} + end + + it 'returns 404 no content found error' do + expect(response).to have_http_status(:not_found) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('user not found with id invalidID') + end + end + end + + context 'with corrupted authorization' do + before do + delete '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => @authToken+"invalidify"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + delete '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + delete '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + delete '/users/'+@user.id.to_s + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/users/get_users_userID_spec.rb b/viscoll-api/spec/requests/users/get_users_userID_spec.rb new file mode 100644 index 00000000..00425ce9 --- /dev/null +++ b/viscoll-api/spec/requests/users/get_users_userID_spec.rb @@ -0,0 +1,103 @@ +require 'rails_helper' + +describe "GET /users/userID", :type => :request do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + context 'with correct authorization' do + context 'and valid params' do + before do + @project1 = Project.create(:title => "first project", :user_id => @user.id) + @project2 = Project.create(:title => "second project", :user_id => @user.id) + @project3 = Project.create(:title => "some other user project", :user_id => "") + get '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => @authToken} + end + + it 'returns a successful ok response' do + expect(response).to have_http_status(:ok) + end + + it 'returns the user object in the response' do + expect(JSON.parse(response.body)['id']).to eq(@user.id.to_s) + expect(JSON.parse(response.body)['email']).to eq("user@mail.com") + expect(JSON.parse(response.body)['name']).to eq("user") + end + + it 'returns all the projects with manuscripts of this user' do + expect(JSON.parse(response.body)['projects'].size).to eq(2) + expect(JSON.parse(response.body)['projects'][0]["title"]).to eq("first project") + expect(JSON.parse(response.body)['projects'][1]["title"]).to eq("second project") + end + end + + context 'and invalid params' do + before do + get '/users/invalidID', params: '', headers: {'Authorization' => @authToken} + end + + it 'returns 404 no content found error' do + expect(response).to have_http_status(:not_found) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('user not found with id invalidID') + end + end + end + + context 'with corrupted authorization' do + before do + get '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => @authToken+"invalidify"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + get '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + get '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + get '/users/'+@user.id.to_s + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/viscoll-api/spec/requests/users/put_users_userID_spec.rb b/viscoll-api/spec/requests/users/put_users_userID_spec.rb new file mode 100644 index 00000000..f4f4fad3 --- /dev/null +++ b/viscoll-api/spec/requests/users/put_users_userID_spec.rb @@ -0,0 +1,265 @@ +require 'rails_helper' + +describe "PUT /users/userID", :type => :request do + before do + @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") + put '/confirmation', params: {:confirmation_token => @user.confirmation_token} + post '/session', params: {:session => { :email=> "user@mail.com", :password => "user" }} + @authToken = JSON.parse(response.body)['session']['jwt'] + end + + context 'with correct authorization' do + context 'and valid params' do + context 'update email address' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:email => "newUser@mail.com"}}, headers: {'Authorization' => @authToken} + end + + it 'returns a successful ok response' do + expect(response).to have_http_status(:ok) + end + + it 'returns the same user object in the response with old email address' do + expect(JSON.parse(response.body)['email']).to eq("user@mail.com") + end + + it 'creates fields for email confirmation in user record' do + expect(User.find(@user.id).confirmation_token).not_to eq(nil) + expect(User.find(@user.id).unconfirmed_email).to eq("newUser@mail.com") + end + end + + context 'update name' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:name => "newUser"}}, headers: {'Authorization' => @authToken} + end + + it 'returns a successful ok response' do + expect(response).to have_http_status(:ok) + end + + it 'returns the updated object in the response with new name' do + expect(JSON.parse(response.body)['name']).to eq("newUser") + end + + it 'updates the field for name in user record' do + expect(User.find(@user.id).name).to eq("newUser") + end + end + + context 'update email and name' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:email => "newUser@mail.com", :name => "newUser"}}, headers: {'Authorization' => @authToken} + end + + it 'returns a successful ok response' do + expect(response).to have_http_status(:ok) + end + + it 'returns the updated object in the response with new name and old email' do + expect(JSON.parse(response.body)['name']).to eq("newUser") + expect(JSON.parse(response.body)['email']).to eq("user@mail.com") + end + + it 'updates the field for name and email confirmation in user record' do + expect(User.find(@user.id).name).to eq("newUser") + expect(User.find(@user.id).confirmation_token).not_to eq(nil) + expect(User.find(@user.id).unconfirmed_email).to eq("newUser@mail.com") + end + end + + context 'update password' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:current_password => "user", :password => "newUser"}}, headers: {'Authorization' => @authToken} + end + + it 'returns a successful ok response' do + expect(response).to have_http_status(:ok) + end + + it 'returns the updated object in the response' do + expect(JSON.parse(response.body)['name']).to eq("user") + end + + it 'updates the field for password in user record' do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "newUser" }} + expect(JSON.parse(response.body)['session']['jwt']).not_to be_empty + end + end + + context 'update email, name and password' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:email => "newUser@mail.com", :name => "newUser", :current_password => "user", :password => "newUser"}}, headers: {'Authorization' => @authToken} + end + + it 'returns a successful ok response' do + expect(response).to have_http_status(:ok) + end + + it 'returns the updated object in the response' do + expect(JSON.parse(response.body)['email']).to eq("user@mail.com") + expect(JSON.parse(response.body)['name']).to eq("newUser") + end + + it 'updates the field for password in user record' do + post '/session', params: {:session => { :email=> "user@mail.com", :password => "newUser" }} + expect(JSON.parse(response.body)['session']['jwt']).not_to be_empty + end + + it 'creates fields for email confirmation in user record' do + expect(User.find(@user.id).confirmation_token).not_to eq(nil) + expect(User.find(@user.id).unconfirmed_email).to eq("newUser@mail.com") + end + + it 'updates the field for name and email confirmation in user record' do + expect(User.find(@user.id).name).to eq("newUser") + expect(User.find(@user.id).confirmation_token).not_to eq(nil) + expect(User.find(@user.id).unconfirmed_email).to eq("newUser@mail.com") + end + end + + + end + + context 'and invalid params' do + context 'with invalid userID' do + before do + put '/users/invalidID', params: '', headers: {'Authorization' => @authToken} + end + + it 'returns 404 no content found error' do + expect(response).to have_http_status(:not_found) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('user not found with id invalidID') + end + end + + context 'with invalid current password' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:current_password => "userInvalid", :password => "newUser"}}, headers: {'Authorization' => @authToken} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['current_password']).to eq(['invalid']) + end + end + + context 'with duplicate email address' do + before do + @user2 = User.create(:name => "newUser", :email => "newUser@mail.com", :password => "newUser") + put '/confirmation', params: {:confirmation_token => @user2.confirmation_token} + put '/users/'+@user.id.to_s, params: {:user => {:email => "newUser@mail.com"}}, headers: {'Authorization' => @authToken} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['email']).to eq(["is already taken"]) + end + end + + context 'with invalid email address' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:email => "invalidEmail"}}, headers: {'Authorization' => @authToken} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['email']).to eq(["is not an email"]) + end + end + + context 'with missing current password' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:password => "newUser"}}, headers: {'Authorization' => @authToken} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['current_password']).to eq(['blank']) + end + end + + context 'with missing new password' do + before do + put '/users/'+@user.id.to_s, params: {:user => {:current_password => "userInvalid", :password => ""}}, headers: {'Authorization' => @authToken} + end + + it 'returns an unprocessable_entity status' do + expect(response).to have_http_status(:unprocessable_entity) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['password']).to eq(['blank']) + end + end + + end + end + + context 'with corrupted authorization' do + before do + put '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => @authToken+"invalidify"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Signature verification raised') + end + end + + context 'with empty authorization' do + before do + put '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => ""} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Nil JSON web token') + end + end + + context 'invalid authorization' do + before do + put '/users/'+@user.id.to_s, params: '', headers: {'Authorization' => "123456789"} + end + + it 'returns an bad request error' do + expect(response).to have_http_status(:bad_request) + end + + it 'returns an appropriate error message' do + expect(JSON.parse(response.body)['error']).to eq('Authorization Token: Not enough or too many segments') + end + end + + context 'without authorization' do + before do + put '/users/'+@user.id.to_s + end + + it 'returns an unauthorized action error' do + expect(response).to have_http_status(:unauthorized) + end + end +end diff --git a/spec/spec_helper.rb b/viscoll-api/spec/spec_helper.rb similarity index 86% rename from spec/spec_helper.rb rename to viscoll-api/spec/spec_helper.rb index 8f698be4..f5a6e864 100644 --- a/spec/spec_helper.rb +++ b/viscoll-api/spec/spec_helper.rb @@ -1,3 +1,19 @@ +# Load and launch SimpleCov at the very top +require 'simplecov' +SimpleCov.start('rails') do + add_filter '/spec/' + add_filter '/config/' + add_filter '/mailers/' + add_filter 'app/channels/application_cable' + add_filter 'app/jobs/application_job.rb' + add_filter 'app/controllers/application_controller.rb' + add_filter 'app/controllers/concerns/rails_jwt_auth/warden_helper.rb' + add_group 'Controllers', 'app/controllers' + add_group 'Models', 'app/models' + add_group 'Helpers', 'app/helpers' +end +# Add webmock +require 'webmock/rspec' # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause @@ -12,9 +28,6 @@ # the additional setup, and require it from the spec files that actually need # it. # -# The `.rspec` file also contains a few flags that are not defaults but that -# users commonly want. -# # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate @@ -47,6 +60,11 @@ # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups + # Clean the test upload directory after the suite + config.after(:suite) do + FileUtils.rm_rf(Dir["#{Rails.root}/uploads/test-files"]) + end + # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. =begin @@ -76,7 +94,7 @@ # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). - config.default_formatter = 'doc' + config.default_formatter = "doc" end # Print the 10 slowest examples and example groups at the diff --git a/lib/assets/.keep b/viscoll-api/tmp/.keep similarity index 100% rename from lib/assets/.keep rename to viscoll-api/tmp/.keep diff --git a/viscoll-app/README.md b/viscoll-app/README.md new file mode 100644 index 00000000..c8ce3927 --- /dev/null +++ b/viscoll-app/README.md @@ -0,0 +1,52 @@ +# VisColl (Redux Front-End) + +## Introduction + +This is the the Redux-driven user interface for Viscoll. + +## System Requirements + +- `node` (>= 6.11.4) +- `npm` (>= 3.10.10) + +### Additional Requirements for Development: + +- [Redux DevTools for Firefox or Chrome](https://github.com/zalmoxisus/redux-devtools-extension) (>= 2.15.1) + +## Setup + +Run this to install the dependencies: +``` +npm install +``` + +Then run the dev server which brings up a browser window serving the user interface: +``` +npm start +``` + +## Testing + +Run this command to test once: +``` +npm test +``` + +Alternatively, run this command to test continually while monitoring for changes: +``` +npm test -- --watch +``` + +## Building + +Before building the app, edit line 3 in `viscoll-app/src/store/axiosConfig.js` to contain the correct root endpoint of the VisColl API: + +```Javascript +export let API_URL = '/api'; + +``` + +Build the app with: +``` +npm build +``` diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/groupActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/groupActions.spec.js new file mode 100644 index 00000000..c73e3fdc --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/groupActions.spec.js @@ -0,0 +1,311 @@ +import { + createGroups, + updateGroup, + updateGroups, + deleteGroup, + deleteGroups, +} from '../../../src/actions/frontend/before/groupActions'; + +import {projectState001} from '../../testData/projectState001' + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test group actions', () => { + it('+++ actionCreator createGroups', () => { + const groupPayload = { + payload: { + request : { + url: `/groups`, + method: 'post', + data: { + "group": { + project_id: "5a57825a4cfad13070870dc3", + title: "None", + type: "Quire", + }, + "additional": { + conjoin: false, + groupIDs: ["123123", "456456"], + leafIDs: ["111", "222"], + sideIDs: ["11", "22", "33", "44"], + memberOrder: 3, + noOfGroups: 2, + noOfLeafs: 1, + order: 5, + } + }, + successMessage: "Successfully added the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.Groups["Group_123123"] = { + id: 'Group_123123', + type: 'Quire', + title: 'None', + tacketed: [], + sewing: [], + nestLevel: 1, + parentID: null, + notes: [], + memberIDs: ["Leaf_111"], + memberType: 'Group', + } + expectedState.project.Groups["Group_456456"] = { + id: 'Group_456456', + type: 'Quire', + title: 'None', + tacketed: [], + sewing: [], + nestLevel: 1, + parentID: null, + notes: [], + memberIDs: ["Leaf_222"], + memberType: 'Group', + } + expectedState.project.Leafs["Leaf_111"] = { + id: 'Leaf_111', + attached_above: 'None', + attached_below: 'None', + conjoined_to: null, + material: 'None', + stub: 'None', + type: 'None', + memberType: 'Leaf', + nestLevel: 1, + notes: [], + parentID: "Group_123123", + rectoID: "Recto_11", + versoID: "Verso_22", + } + expectedState.project.Leafs["Leaf_222"] = { + id: 'Leaf_222', + attached_above: 'None', + attached_below: 'None', + conjoined_to: null, + material: 'None', + stub: 'None', + type: 'None', + memberType: 'Leaf', + nestLevel: 1, + notes: [], + parentID: "Group_456456", + rectoID: "Recto_33", + versoID: "Verso_44", + } + expectedState.project.Rectos["Recto_11"] = { + id: "Recto_11", + parentID: "Leaf_111", + folio_number: null, + script_direction: 'None', + texture: 'Hair', + image: {}, + notes: [], + memberType: 'Recto', + } + expectedState.project.Rectos["Recto_33"] = { + id: "Recto_33", + parentID: "Leaf_222", + folio_number: null, + script_direction: 'None', + texture: 'Hair', + image: {}, + notes: [], + memberType: 'Recto', + } + expectedState.project.Versos["Verso_22"] = { + id: "Verso_22", + parentID: "Leaf_111", + folio_number: null, + script_direction: 'None', + texture: 'Flesh', + image: {}, + notes: [], + memberType: 'Verso', + } + expectedState.project.Versos["Verso_44"] = { + id: "Verso_44", + parentID: "Leaf_222", + folio_number: null, + script_direction: 'None', + texture: 'Flesh', + image: {}, + notes: [], + memberType: 'Verso', + } + expectedState.project.groupIDs.push("Group_123123"); + expectedState.project.groupIDs.push("Group_456456"); + expectedState.project.leafIDs.push("Leaf_111"); + expectedState.project.leafIDs.push("Leaf_222"); + expectedState.project.rectoIDs.push("Recto_11"); + expectedState.project.rectoIDs.push("Recto_33"); + expectedState.project.versoIDs.push("Verso_22"); + expectedState.project.versoIDs.push("Verso_44"); + expectedState.collationManager.flashItems.groups.push("Group_123123"); + expectedState.collationManager.flashItems.groups.push("Group_456456"); + expectedState.collationManager.flashItems.leaves.push("Leaf_111"); + expectedState.collationManager.flashItems.leaves.push("Leaf_222"); + const gotState = createGroups(groupPayload, beforeState); + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator updateGroup', () => { + const groupPayload = { + payload: { + request : { + url: `/groups/Group_5a57825a4cfad13070870df4`, + method: 'put', + data: { + "group": { + title: "New title", + type: "Booklet", + }, + }, + successMessage: "Successfully updated the group" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].type = "Booklet"; + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].title = "New title"; + const gotState = updateGroup(groupPayload, beforeState); + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator updateGroups', () => { + const groupPayload = { + payload: { + request : { + url: `/groups`, + method: 'put', + data: { + groups: [ + { + id: "Group_5a57825a4cfad13070870df4", + attributes: { + title: "New title", + type: "Booklet", + } + }, + { + id: "Group_5a57825a4cfad13070870df5", + attributes: { + title: "New title 2", + type: "Booklet", + } + } + ] + }, + successMessage: "Successfully updated the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].type = "Booklet"; + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].title = "New title"; + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].type = "Booklet"; + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].title = "New title 2"; + const gotState = updateGroups(groupPayload, beforeState); + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator deleteGroup', () => { + const groupPayload = { + payload: { + request : { + url: `/groups/Group_5a57825a4cfad13070870df7`, + method: 'delete', + successMessage: "Successfully deleted the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + delete expectedState.project.Groups["Group_5a57825a4cfad13070870df7"]; + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd6"]; + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd9"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870dd7"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870dda"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870dd8"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870ddb"]; + expectedState.project.groupIDs.splice(-1); + expectedState.project.leafIDs.splice(expectedState.project.leafIDs.indexOf("Leaf_5a57825a4cfad13070870dd6"), 1); + expectedState.project.leafIDs.splice(expectedState.project.leafIDs.indexOf("Leaf_5a57825a4cfad13070870dd9"), 1); + expectedState.project.rectoIDs.splice(expectedState.project.rectoIDs.indexOf("Recto_5a57825a4cfad13070870dd7"), 1); + expectedState.project.rectoIDs.splice(expectedState.project.rectoIDs.indexOf("Recto_5a57825a4cfad13070870dda"), 1); + expectedState.project.versoIDs.splice(expectedState.project.versoIDs.indexOf("Verso_5a57825a4cfad13070870dd8"), 1); + expectedState.project.versoIDs.splice(expectedState.project.versoIDs.indexOf("Verso_5a57825a4cfad13070870ddb"), 1); + expectedState.collationManager.selectedObjects.lastSelected = ""; + expectedState.collationManager.selectedObjects.members = []; + expectedState.collationManager.selectedObjects.type = ""; + expectedState.project.Groups["Group_5a57825a4cfad13070870df6"].memberIDs.splice(0,1); + const gotState = deleteGroup("Group_5a57825a4cfad13070870df7", beforeState); + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator deleteGroups', () => { + const groupPayload = { + payload: { + request : { + url: `/groups`, + method: 'delete', + data: { + groups: [ + "Group_5a57825a4cfad13070870df6", + "Group_5a57825a4cfad13070870df7" + ] + }, + successMessage: "Successfully deleted the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + delete expectedState.project.Groups["Group_5a57825a4cfad13070870df6"]; + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870ddc"]; + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870ddf"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870ddd"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870de0"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870dde"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870de1"]; + + delete expectedState.project.Groups["Group_5a57825a4cfad13070870df7"]; + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd6"]; + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd9"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870dd7"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870dda"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870dd8"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870ddb"]; + expectedState.project.groupIDs.splice(expectedState.project.groupIDs.indexOf("Group_5a57825a4cfad13070870df6"), 1); + expectedState.project.groupIDs.splice(expectedState.project.groupIDs.indexOf("Group_5a57825a4cfad13070870df7"), 1); + expectedState.project.leafIDs.splice(expectedState.project.leafIDs.indexOf("Leaf_5a57825a4cfad13070870ddc"), 1); + expectedState.project.leafIDs.splice(expectedState.project.leafIDs.indexOf("Leaf_5a57825a4cfad13070870ddf"), 1); + expectedState.project.leafIDs.splice(expectedState.project.leafIDs.indexOf("Leaf_5a57825a4cfad13070870dd6"), 1); + expectedState.project.leafIDs.splice(expectedState.project.leafIDs.indexOf("Leaf_5a57825a4cfad13070870dd9"), 1); + expectedState.project.rectoIDs.splice(expectedState.project.rectoIDs.indexOf("Recto_5a57825a4cfad13070870ddd"), 1); + expectedState.project.rectoIDs.splice(expectedState.project.rectoIDs.indexOf("Recto_5a57825a4cfad13070870de0"), 1); + expectedState.project.rectoIDs.splice(expectedState.project.rectoIDs.indexOf("Recto_5a57825a4cfad13070870dd7"), 1); + expectedState.project.rectoIDs.splice(expectedState.project.rectoIDs.indexOf("Recto_5a57825a4cfad13070870dda"), 1); + expectedState.project.versoIDs.splice(expectedState.project.versoIDs.indexOf("Verso_5a57825a4cfad13070870dde"), 1); + expectedState.project.versoIDs.splice(expectedState.project.versoIDs.indexOf("Verso_5a57825a4cfad13070870de1"), 1); + expectedState.project.versoIDs.splice(expectedState.project.versoIDs.indexOf("Verso_5a57825a4cfad13070870dd8"), 1); + expectedState.project.versoIDs.splice(expectedState.project.versoIDs.indexOf("Verso_5a57825a4cfad13070870ddb"), 1); + expectedState.collationManager.selectedObjects.lastSelected = ""; + expectedState.collationManager.selectedObjects.members = []; + expectedState.collationManager.selectedObjects.type = ""; + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].memberIDs.splice(0,1); + const gotState = deleteGroups(["Group_5a57825a4cfad13070870df6", "Group_5a57825a4cfad13070870df7"], beforeState); + expect(gotState).toEqual(expectedState) + }) + +}) \ No newline at end of file diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/helperActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/helperActions.spec.js new file mode 100644 index 00000000..5b85c1d0 --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/helperActions.spec.js @@ -0,0 +1,48 @@ +import { + getLeafMembers, +} from '../../../src/actions/frontend/before/helperActions'; + +import {projectState001} from '../../testData/projectState001'; + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test helper actions', () => { + + it('+++ actionCreator getLeafMembers', () => { + // Test getLeafMembers of 2nd group (Group_5a57825a4cfad13070870df5) + const memberIDs = [ + 'Group_5a57825a4cfad13070870df6', + 'Leaf_5a57825a4cfad13070870de2', + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb', + 'Leaf_5a57825a4cfad13070870dee', + 'Leaf_5a57825a4cfad13070870df1' + ]; + const leafIDs = []; + const projectState = cloneDeep(projectState001); + const expectedLeafIDs = [ + 'Leaf_5a57825a4cfad13070870dd6', + 'Leaf_5a57825a4cfad13070870dd9', + 'Leaf_5a57825a4cfad13070870ddc', + 'Leaf_5a57825a4cfad13070870ddf', + 'Leaf_5a57825a4cfad13070870de2', + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb', + 'Leaf_5a57825a4cfad13070870dee', + 'Leaf_5a57825a4cfad13070870df1', + ] + getLeafMembers(memberIDs, projectState, leafIDs); + expect(leafIDs).toEqual(expectedLeafIDs); + }) + it('+++ actionCreator getLeafMembers', () => { + // Test getLeafMembers of an empty group + const memberIDs = []; + const leafIDs = []; + const projectState = cloneDeep(projectState001); + const expectedLeafIDs = [] + getLeafMembers(memberIDs, projectState, leafIDs); + expect(leafIDs).toEqual(expectedLeafIDs); + }) +}) \ No newline at end of file diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/imageActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/imageActions.spec.js new file mode 100644 index 00000000..dffba831 --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/imageActions.spec.js @@ -0,0 +1,112 @@ +import { + linkImages, + unlinkImages, + deleteImages, +} from '../../../src/actions/frontend/before/imageActions'; + +import {projectState001} from '../../testData/projectState001' +import {dashboardState001} from '../../testData/dashboardState001' + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test image actions', () => { + // Test linkImagesFromProject and linkImagesFromDashboard + it('+++ actionCreator linkImages', () => { + const imagePayload = { + payload: { + request : { + url: `/images/link`, + method: 'put', + data: { + "imageIDs": ['5a5783154cfad16535870e13'], + "projectIDs": ['5a57825a4cfad13070870dc3'], + }, + successMessage: "Successfully linked the images", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeDashState = cloneDeep(dashboardState001); + const beforeState = cloneDeep(projectState001); + let expectedDashState = cloneDeep(dashboardState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.manifests.DIYImages.images.push({ + label: '103496018.jpeg', + url: 'http://localhost:3001/images/5a5783154cfad16535870e13_103496018.jpeg', + manifestID: 'DIYImages' + }); + expectedDashState.images[6].projectIDs.push('5a57825a4cfad13070870dc3'); + + const gotState = linkImages(imagePayload, beforeDashState, beforeState); + expect(gotState.active).toEqual(expectedState) + expect(gotState.dashboard).toEqual(expectedDashState) + }) + + // Test unlinkImagesFromProject and unlinkImagesFromDashboard + it('+++ actionCreator unlinkImages', () => { + const imagePayload = { + payload: { + request : { + url: `/images/unlink`, + method: 'put', + data: { + "imageIDs": ['5a5cc9594cfad17bed092f4c', '5a5cc95a4cfad17bed092f4e'], + "projectIDs": ['5a57825a4cfad13070870dc3'], + }, + successMessage: "Successfully unlinked the images", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeDashState = cloneDeep(dashboardState001); + const beforeState = cloneDeep(projectState001); + let expectedDashState = cloneDeep(dashboardState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.manifests.DIYImages.images.splice(2,1) + expectedState.project.manifests.DIYImages.images.splice(3,1) + expectedState.project.Versos['Verso_5a57825a4cfad13070870dcc'].image = {}; + + expectedDashState.images[2].projectIDs.splice(0,1); + expectedDashState.images[4].projectIDs.splice(0,1); + + const gotState = unlinkImages(imagePayload, beforeDashState, beforeState); + expect(gotState.active).toEqual(expectedState) + expect(gotState.dashboard).toEqual(expectedDashState) + }) + + // Test deleteImagesFromDashboard + it('+++ actionCreator deleteImages', () => { + const imagePayload = { + payload: { + request : { + url: `/images`, + method: 'delete', + data: { + "imageIDs": ['5a5cc9594cfad17bed092f4c', '5a5cc95a4cfad17bed092f4e'], + }, + successMessage: "Successfully deleted the images", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeDashState = cloneDeep(dashboardState001); + const beforeState = cloneDeep(projectState001); + let expectedDashState = cloneDeep(dashboardState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.manifests.DIYImages.images.splice(2,1) + expectedState.project.manifests.DIYImages.images.splice(3,1) + expectedState.project.Versos['Verso_5a57825a4cfad13070870dcc'].image = {}; + + expectedDashState.images.splice(2,1); + expectedDashState.images.splice(3,1); + + const gotState = deleteImages(imagePayload, beforeDashState, beforeState); + + expect(gotState.active).toEqual(expectedState) + expect(gotState.dashboard).toEqual(expectedDashState) + }) + +}) \ No newline at end of file diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/leafActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/leafActions.spec.js new file mode 100644 index 00000000..c2a7a2c8 --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/leafActions.spec.js @@ -0,0 +1,359 @@ +import { + createLeaves, + updateLeaf, + updateLeaves, + deleteLeaf, + deleteLeaves, + autoConjoinLeafs, + generateFolioPageNumbers, +} from '../../../src/actions/frontend/before/leafActions'; + +import {projectState001} from '../../testData/projectState001' + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test leaf actions', () => { + it('+++ actionCreator createLeaves', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs`, + method: 'post', + data: { + "leaf": { + project_id: "5a57825a4cfad13070870dc3", + parentID: "Group_5a57825a4cfad13070870df5", + }, + "additional": { + conjoin: false, + leafIDs: ["111"], + sideIDs: ["11", "22"], + memberOrder: 8, + noOfLeafs: 1, + order: 17, + } + }, + successMessage: "Successfully added the leaves" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].memberIDs.push("Leaf_111"); + expectedState.project.Leafs["Leaf_111"] = { + id: 'Leaf_111', + attached_above: 'None', + attached_below: 'None', + conjoined_to: null, + material: 'None', + stub: 'None', + type: 'None', + memberType: 'Leaf', + nestLevel: 1, + notes: [], + parentID: "Group_5a57825a4cfad13070870df5", + rectoID: "Recto_11", + versoID: "Verso_22", + } + expectedState.project.Rectos["Recto_11"] = { + id: "Recto_11", + parentID: "Leaf_111", + folio_number: null, + script_direction: 'None', + texture: 'Hair', + image: {}, + notes: [], + memberType: 'Recto', + } + expectedState.project.Versos["Verso_22"] = { + id: "Verso_22", + parentID: "Leaf_111", + folio_number: null, + script_direction: 'None', + texture: 'Flesh', + image: {}, + notes: [], + memberType: 'Verso', + } + expectedState.project.leafIDs.push("Leaf_111"); + expectedState.project.rectoIDs.push("Recto_11"); + expectedState.project.versoIDs.push("Verso_22"); + expectedState.collationManager.flashItems.leaves.push("Leaf_111"); + const gotState = createLeaves(leafPayload, beforeState); + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator updateLeaf', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs/Leaf_5a57825a4cfad13070870dc4`, + method: 'post', + data: { + "leaf": { + material: "Parchment", + } + }, + successMessage: "Successfully updated the leaf" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc4"].material = "Parchment"; + const gotState = updateLeaf(leafPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) + it('+++ actionCreator updateLeaves', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs`, + method: 'post', + data: { + project_id: "5a57825a4cfad13070870dc3", + leafs: [ + { + id: "Leaf_5a57825a4cfad13070870dc4", + attributes: { + material: "Parchment", + type: "Added" + } + }, + { + id: "Leaf_5a57825a4cfad13070870dc7", + attributes: { + material: "Parchment", + type: "Added" + } + }, + ] + }, + successMessage: "Successfully updated the leaves" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc4"].material = "Parchment"; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc7"].material = "Parchment"; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc4"].type = "Added"; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc7"].type = "Added"; + const gotState = updateLeaves(leafPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator deleteLeaf', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs/Leaf_5a57825a4cfad13070870dc4`, + method: 'delete', + successMessage: "Successfully deleted the leaf" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc4"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc5"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870dc6"]; + + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd3"].conjoined_to = null; + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].memberIDs.splice(0,1); + expectedState.project.Notes["5a57825a4cfad13070870df9"].objects.Leaf.splice(0,1); + expectedState.project.Notes["5a57825a4cfad13070870df9"].objects.Verso.splice(0,1); + expectedState.project.Notes["5a57825a4cfad13070870dfa"].objects.Verso.splice(0,1); + expectedState.project.leafIDs.splice(0,1); + expectedState.project.rectoIDs.splice(0,1); + expectedState.project.versoIDs.splice(0,1); + + expectedState.collationManager.selectedObjects.lastSelected = ""; + expectedState.collationManager.selectedObjects.members = []; + expectedState.collationManager.selectedObjects.type = ""; + + const gotState = deleteLeaf("Leaf_5a57825a4cfad13070870dc4", beforeState); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator deleteLeaves', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs`, + method: 'delete', + data: { + leafs: [ + "Leaf_5a57825a4cfad13070870dc4", + "Leaf_5a57825a4cfad13070870df1" + ] + }, + successMessage: "Successfully deleted the leaves" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + // Delete first leaf + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc4"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc5"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870dc6"]; + + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd3"].conjoined_to = null; + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].memberIDs.splice(0,1); + expectedState.project.Notes["5a57825a4cfad13070870df9"].objects.Leaf.splice(0,1); + expectedState.project.Notes["5a57825a4cfad13070870df9"].objects.Verso.splice(0,1); + expectedState.project.Notes["5a57825a4cfad13070870dfa"].objects.Verso.splice(0,1); + expectedState.project.leafIDs.splice(0,1); + expectedState.project.rectoIDs.splice(0,1); + expectedState.project.versoIDs.splice(0,1); + + // Delete second leaf + delete expectedState.project.Leafs["Leaf_5a57825a4cfad13070870df1"]; + delete expectedState.project.Rectos["Recto_5a57825a4cfad13070870df2"]; + delete expectedState.project.Versos["Verso_5a57825a4cfad13070870df3"]; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870de2"].conjoined_to = null; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dee"].attached_below = "None"; + + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].memberIDs.splice(-1,1); + expectedState.project.leafIDs.splice(-1,1); + expectedState.project.rectoIDs.splice(-1,1); + expectedState.project.versoIDs.splice(-1,1); + + expectedState.collationManager.selectedObjects.lastSelected = ""; + expectedState.collationManager.selectedObjects.members = []; + expectedState.collationManager.selectedObjects.type = ""; + + const gotState = deleteLeaves(["Leaf_5a57825a4cfad13070870dc4", "Leaf_5a57825a4cfad13070870df1"], beforeState); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator autoConjoinLeafs', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs/conjoin`, + method: 'put', + data: { + leafs: [ + "Leaf_5a57825a4cfad13070870dd9", + "Leaf_5a57825a4cfad13070870dd6" + ] + }, + successMessage: "Successfully conjoined the leaves" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd9"].conjoined_to = "Leaf_5a57825a4cfad13070870dd6"; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd6"].conjoined_to = "Leaf_5a57825a4cfad13070870dd9"; + + const gotState = autoConjoinLeafs(leafPayload, beforeState, ["Leaf_5a57825a4cfad13070870dd9","Leaf_5a57825a4cfad13070870dd6"]); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator autoConjoinLeafs for odd number of leaves', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs/conjoin`, + method: 'put', + data: { + leafs: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dca', + ] + }, + successMessage: "Successfully conjoined the leaves" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc4"].conjoined_to = "Leaf_5a57825a4cfad13070870dca"; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd3"].conjoined_to = null; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dc7"].conjoined_to = null; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dd0"].conjoined_to = null; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dca"].conjoined_to = "Leaf_5a57825a4cfad13070870dc4"; + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dcd"].conjoined_to = null; + + const gotState = autoConjoinLeafs(leafPayload, beforeState, ["Leaf_5a57825a4cfad13070870dc4","Leaf_5a57825a4cfad13070870dc7","Leaf_5a57825a4cfad13070870dca"]); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator generateFolioPageNumbers', () => { + const leafPayload = { + payload: { + request : { + url: `/leafs/generateFolio`, + method: 'put', + data: { + startNumber: 3, + rectoIDs: [ + 'Recto_5a57825a4cfad13070870dc8', + 'Recto_5a57825a4cfad13070870dcb', + 'Recto_5a57825a4cfad13070870dce', + 'Recto_5a57825a4cfad13070870dd1', + 'Recto_5a57825a4cfad13070870df2' + ], + versoIDs: [ + 'Verso_5a57825a4cfad13070870dc9', + 'Verso_5a57825a4cfad13070870dcc', + 'Verso_5a57825a4cfad13070870dcf', + 'Verso_5a57825a4cfad13070870dd2', + 'Verso_5a57825a4cfad13070870df3' + ], + }, + successMessage: "Successfully generated the folio numbers" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + let gotState = generateFolioPageNumbers(leafPayload, beforeState, "folio_number"); + leafPayload.payload.request.data.startNumber = 6; + gotState = generateFolioPageNumbers(leafPayload, gotState, "page_number"); + + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc8"].folio_number = "3R"; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dcb"].folio_number = "4R"; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dce"].folio_number = "5R"; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dd1"].folio_number = "6R"; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870df2"].folio_number = "7R"; + + expectedState.project.Versos["Verso_5a57825a4cfad13070870dc9"].folio_number = "3V"; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dcc"].folio_number = "4V"; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dcf"].folio_number = "5V"; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dd2"].folio_number = "6V"; + expectedState.project.Versos["Verso_5a57825a4cfad13070870df3"].folio_number = "7V"; + + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc8"].page_number = 6; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dcb"].page_number = 8; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dce"].page_number = 10; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dd1"].page_number = 12; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870df2"].page_number = 14; + + expectedState.project.Versos["Verso_5a57825a4cfad13070870dc9"].page_number = 7; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dcc"].page_number = 9; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dcf"].page_number = 11; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dd2"].page_number = 13; + expectedState.project.Versos["Verso_5a57825a4cfad13070870df3"].page_number = 15; + + expect(gotState).toEqual(expectedState); + }) + +}) \ No newline at end of file diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/manifestActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/manifestActions.spec.js new file mode 100644 index 00000000..ca50dd59 --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/manifestActions.spec.js @@ -0,0 +1,69 @@ +import { + updateManifest, + deleteManifest, +} from '../../../src/actions/frontend/before/manifestActions'; + +import {projectState001} from '../../testData/projectState001'; + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test manifest actions', () => { + + it('+++ actionCreator updateManifest', () => { + const manifestPayload = { + payload: { + request : { + url: `/projects/5a57825a4cfad13070870dc3/manifests`, + method: 'put', + data: { + "manifest": { + "id": "5a25b0703b0eb7478b415bd4", + "name": "new manifest name", + } + }, + successMessage: "Successfully updated the manifest", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.manifests["5a25b0703b0eb7478b415bd4"].name = "new manifest name"; + + const gotState = updateManifest(manifestPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator deleteManifest', () => { + const manifestPayload = { + payload: { + request : { + url: `/projects/5a57825a4cfad13070870dc3/manifests`, + method: 'delete', + data: { + "manifest": { + "id": "5a25b0703b0eb7478b415bd4", + } + }, + successMessage: "Successfully deleted the manifest", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + delete expectedState.project.manifests["5a25b0703b0eb7478b415bd4"]; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc5"].image = {}; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc8"].image = {}; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dcb"].image = {}; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dce"].image = {}; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dd1"].image = {}; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dd4"].image = {}; + + const gotState = deleteManifest(manifestPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) + +}) \ No newline at end of file diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/noteActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/noteActions.spec.js new file mode 100644 index 00000000..688e596e --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/noteActions.spec.js @@ -0,0 +1,250 @@ +import { + createNoteType, + updateNoteType, + deleteNoteType, + createNote, + updateNote, + linkNote, + unlinkNote, + deleteNote, +} from '../../../src/actions/frontend/before/noteActions'; + +import {projectState001} from '../../testData/projectState001' + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test note actions', () => { + + it('+++ actionCreator createNoteType', () => { + const noteTypePayload = { + payload: { + request : { + url: `/notes/type`, + method: 'post', + data: { + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Watermark" + } + }, + successMessage: "Successfully created the note type" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + const createNoteTypeAction = createNoteType(noteTypePayload, beforeState); + let afterState = cloneDeep(projectState001); + afterState.project.noteTypes.push("Watermark"); + expect(createNoteTypeAction).toEqual(afterState); + }) + + it('+++ actionCreator updateNoteType', () => { + const noteTypePayload = { + payload: { + request: { + url: '/notes/type', + method: 'put', + data: { + noteType: { + project_id: "5951303fc9bf3c7b9a573a3f", + old_type: 'Damage', + type: 'Damages', + } + }, + successMessage: "Successfully updated the note type", + errorMessage: "Oops! Something went wrong", + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(projectState001) + expectedState.project.noteTypes[3] = 'Damages' + expectedState.project.Notes['5a57825a4cfad13070870dfa'].type = 'Damages' + let gotState = updateNoteType(noteTypePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator deleteNoteType', () => { + const noteTypePayload = { + payload: { + request: { + url: '/notes/type', + method: 'delete', + data: { + noteType: { + project_id: "5951303fc9bf3c7b9a573a3f", + type: "Hand" + } + }, + successMessage: "Successfully deleted the note type", + errorMessage: "Oops! Something went wrong", + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(projectState001) + expectedState.project.noteTypes = ['Unknown', 'Ink', 'Damage'] + expectedState.project.Notes['5a57825a4cfad13070870df9'].type = 'Unknown' + let gotState = deleteNoteType(noteTypePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator createNote', () => { + const notePayload = { + payload: { + request: { + url: '/notes', + method: 'post', + data: { + note: { + id: "f951303fc9bf3c7b9a573a3f", + project_id: "5951303fc9bf3c7b9a573a3f", + title: "Example Note", + type: "asd", + description: "example content", + show: true, + } + }, + successMessage: "", + errorMessage: "" + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(beforeState) + expectedState.project.Notes["f951303fc9bf3c7b9a573a3f"] = { + id: "f951303fc9bf3c7b9a573a3f", + title: "Example Note", + type: "asd", + description: "example content", + show: true, + objects: { Group: [], Leaf: [], Recto: [], Verso: [] } + } + let gotState = createNote(notePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator updateNote', () => { + const notePayload = { + payload: { + request: { + url: '/notes/5a57825a4cfad13070870df8', + method: 'put', + data: { + note: { + description: "Some lot of black ink over here", + title: "Black inks", + type: "Ink" + } + }, + successMessage: "", + errorMessage: "" + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(beforeState) + expectedState.project.Notes["5a57825a4cfad13070870df8"].title = "Black inks" + expectedState.project.Notes["5a57825a4cfad13070870df8"].type = "Ink" + expectedState.project.Notes["5a57825a4cfad13070870df8"].description = "Some lot of black ink over here" + let gotState = updateNote(notePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator linkNote', () => { + const notePayload = { + payload: { + request: { + url: '/notes/5a57825a4cfad13070870df8/link', + method: 'put', + data: { + objects: [ + { + type: "Verso", + id: "Verso_5a57825a4cfad13070870dc6", + }, + { + type: "Leaf", + id: "Leaf_5a57825a4cfad13070870dee", + }, + { + type: "Group", + id: "Group_5a57825a4cfad13070870df6", + } + ] + }, + successMessage: "", + errorMessage: "" + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(beforeState) + expectedState.project.Notes["5a57825a4cfad13070870df8"].objects.Group.push("Group_5a57825a4cfad13070870df6") + expectedState.project.Notes["5a57825a4cfad13070870df8"].objects.Leaf.push("Leaf_5a57825a4cfad13070870dee") + expectedState.project.Notes["5a57825a4cfad13070870df8"].objects.Verso.push("Verso_5a57825a4cfad13070870dc6") + expectedState.project.Groups["Group_5a57825a4cfad13070870df6"].notes.push("5a57825a4cfad13070870df8") + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870dee"].notes.push("5a57825a4cfad13070870df8") + expectedState.project.Versos["Verso_5a57825a4cfad13070870dc6"].notes.push("5a57825a4cfad13070870df8") + let gotState = linkNote(notePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator unlinkNote', () => { + const notePayload = { + payload: { + request: { + url: '/notes/5a57825a4cfad13070870df8/unlink', + method: 'put', + data: { + objects: [ + { + type: "Group", + id: "Group_5a57825a4cfad13070870df5", + }, + { + type: "Leaf", + id: "Leaf_5a57825a4cfad13070870de8", + }, + ] + }, + successMessage: "", + errorMessage: "" + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(beforeState) + expectedState.project.Notes["5a57825a4cfad13070870df8"].objects.Group.splice(-1,1) + expectedState.project.Notes["5a57825a4cfad13070870df8"].objects.Leaf.splice(1,1) + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].notes = [] + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870de8"].notes = [] + let gotState = unlinkNote(notePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator deleteNote', () => { + const notePayload = { + payload: { + request: { + url: '/notes/5a57825a4cfad13070870df8', + method: 'delete', + successMessage: "", + errorMessage: "" + } + } + } + const beforeState = cloneDeep(projectState001) + let expectedState = cloneDeep(beforeState) + delete expectedState.project.Notes["5a57825a4cfad13070870df8"] + expectedState.project.Groups["Group_5a57825a4cfad13070870df4"].notes = [] + expectedState.project.Groups["Group_5a57825a4cfad13070870df5"].notes = [] + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870de5"].notes = [] + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870de8"].notes = [] + expectedState.project.Leafs["Leaf_5a57825a4cfad13070870deb"].notes = [] + let gotState = deleteNote(notePayload, beforeState) + expect(gotState).toEqual(expectedState) + }) + +}) diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/projectActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/projectActions.spec.js new file mode 100644 index 00000000..5a2f46a3 --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/projectActions.spec.js @@ -0,0 +1,94 @@ +import { + updateProject, + deleteProject, +} from '../../../src/actions/frontend/before/projectActions'; + +import {dashboardState001} from '../../testData/dashboardState001' + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test project actions', () => { + + it('+++ actionCreator updateProject', () => { + const projectPayload = { + payload: { + request : { + url: `/projects/5a57825a4cfad13070870dc3`, + method: 'put', + data: { + "project": { + "title": "my prject 123123", + "shelfmark": "mss 568456", + "metadata": { + "date": "18th century" + } + } + }, + successMessage: "Successfully linked the images", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(dashboardState001); + let expectedState = cloneDeep(dashboardState001); + + expectedState.projects[0].title = "my prject 123123"; + expectedState.projects[0].shelfmark = "mss 568456"; + expectedState.projects[0].metadata.date = "18th century"; + + const gotState = updateProject(projectPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator deleteProject delete images', () => { + const projectPayload = { + payload: { + request : { + url: `/projects/5a57825a4cfad13070870dc3`, + method: 'delete', + data: { + "deleteUnlinkedImages": true + }, + successMessage: "Successfully linked the images", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(dashboardState001); + let expectedState = cloneDeep(dashboardState001); + + expectedState.projects.splice(0,1); + expectedState.images.splice(0, 6); + + const gotState = deleteProject(projectPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) + + it('+++ actionCreator deleteProject do not delete images', () => { + const projectPayload = { + payload: { + request : { + url: `/projects/5a57825a4cfad13070870dc3`, + method: 'delete', + data: { + "deleteUnlinkedImages": false + }, + successMessage: "Successfully linked the images", + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(dashboardState001); + let expectedState = cloneDeep(dashboardState001); + + expectedState.projects.splice(0,1); + for (let i in expectedState.images) { + if (expectedState.images[i].projectIDs.includes('5a57825a4cfad13070870dc3')) { + expectedState.images[i].projectIDs.splice(0,1); + } + } + + const gotState = deleteProject(projectPayload, beforeState); + expect(gotState).toEqual(expectedState); + }) +}) \ No newline at end of file diff --git a/viscoll-app/__test__/actions/frontendBeforeActions/sideActions.spec.js b/viscoll-app/__test__/actions/frontendBeforeActions/sideActions.spec.js new file mode 100644 index 00000000..99cb176a --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/sideActions.spec.js @@ -0,0 +1,148 @@ +import { + updateSide, + updateSides, + mapSides, + generateFolioNumbers, +} from '../../../src/actions/frontend/before/sideActions'; + +import {projectState001} from '../../testData/projectState001' +import {dashboardState001} from '../../testData/dashboardState001' + +import {cloneDeep} from 'lodash'; + +describe('>>>A C T I O N --- Test side actions', () => { + it('+++ actionCreator updateSide', () => { + const sidePayload = { + payload: { + request : { + url: `/sides/Recto_5a57825a4cfad13070870dc8`, + method: 'put', + data: { + "side": { + texture: "Felt", + }, + }, + successMessage: "Successfully updated the side" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc8"].texture = "Felt"; + + const gotState = updateSide(sidePayload, beforeState); + expect(gotState).toEqual(expectedState) + }) + + it('+++ actionCreator updateSides', () => { + const sidePayload = { + payload: { + request : { + url: `/sides`, + method: 'put', + data: { + "sides": [ + { + id: "Verso_5a57825a4cfad13070870dcc", + attributes: { script_direction: "Top-To-Bottom"}, + }, + { + id: "Verso_5a57825a4cfad13070870dc9", + attributes: { script_direction: "Top-To-Bottom"}, + }, + { + id: "Verso_5a57825a4cfad13070870dc6", + attributes: { script_direction: "Right-To-Left"}, + }, + ] + }, + successMessage: "Successfully updated the side" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + + expectedState.project.Versos["Verso_5a57825a4cfad13070870dcc"].script_direction = "Top-To-Bottom"; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dc9"].script_direction = "Top-To-Bottom"; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dc6"].script_direction = "Right-To-Left"; + + const gotState = updateSides(sidePayload, beforeState); + expect(gotState).toEqual(expectedState) + }) + it('+++ actionCreator mapSides', () => { + const sidePayload = { + payload: { + request : { + url: `/sides`, + method: 'put', + data: { + "sides": [ + { + "id": "Recto_5a57825a4cfad13070870ddd", + "attributes": { + "image": { + "manifestID": "DIYImages", + "label": "cguk1l0u4aeewdf.jpeg", + "url": "http://localhost:3001/images/5a5cc9594cfad17bed092f4a_cguk1l0u4aeewdf.jpeg" + } + } + }, + { + "id": "Verso_5a57825a4cfad13070870de1", + "attributes": { + "image": { + "label": "Hollar_a_3000_0005", + "url": "https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005", + "manifestID": "5a25b0703b0eb7478b415bd4" + } + } + }, + { + "id": "Recto_5a57825a4cfad13070870dc5", + "attributes": { + "image": {} + } + }, + { + "id": "Verso_5a57825a4cfad13070870dc6", + "attributes": { + "image": {} + } + } + ] + }, + successMessage: "Successfully updated the side" , + errorMessage: "Ooops! Something went wrong" + } + } + } + const beforeDashState = cloneDeep(dashboardState001); + const beforeState = cloneDeep(projectState001); + let expectedState = cloneDeep(projectState001); + const expectedDashState = cloneDeep(dashboardState001); + + expectedState.project.Rectos["Recto_5a57825a4cfad13070870ddd"].image = { + "manifestID": "DIYImages", + "label": "cguk1l0u4aeewdf.jpeg", + "url": "http://localhost:3001/images/5a5cc9594cfad17bed092f4a_cguk1l0u4aeewdf.jpeg" + }; + expectedState.project.Versos["Verso_5a57825a4cfad13070870de1"].image = { + "label": "Hollar_a_3000_0005", + "url": "https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005", + "manifestID": "5a25b0703b0eb7478b415bd4" + }; + expectedState.project.Rectos["Recto_5a57825a4cfad13070870dc5"].image = {}; + expectedState.project.Versos["Verso_5a57825a4cfad13070870dc6"].image = {}; + + expectedDashState.images[0].sideIDs.splice(0,1); + expectedDashState.images[0].sideIDs.push("Recto_5a57825a4cfad13070870ddd"); + + const gotState = mapSides(sidePayload, beforeState, beforeDashState); + expect(gotState.active).toEqual(expectedState) + expect(gotState.dashboard).toEqual(expectedDashState) + }) +}) \ No newline at end of file diff --git a/viscoll-app/__test__/testData/dashboardState001.js b/viscoll-app/__test__/testData/dashboardState001.js new file mode 100644 index 00000000..ba0e2687 --- /dev/null +++ b/viscoll-app/__test__/testData/dashboardState001.js @@ -0,0 +1,78 @@ +export const dashboardState001 = { + projects: [ + { + id: '5a57825a4cfad13070870dc3', + title: 'my prject', + shelfmark: 'mss 568', + metadata: { + date: '19th century' + }, + created_at: '2018-01-12T19:05:20.803Z', + updated_at: '2018-01-12T19:05:21.175Z' + }, + ], + images: [ + { + id: '5a5cc9594cfad17bed092f4a', + projectIDs: [ + '5a57825a4cfad13070870dc3' + ], + sideIDs: ['Verso_5a57825a4cfad13070870dc6'], + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4a_cguk1l0u4aeewdf.jpeg', + label: 'cguk1l0u4aeewdf.jpeg' + }, + { + id: '5a5cc9594cfad17bed092f4b', + projectIDs: [ + '5a57825a4cfad13070870dc3' + ], + sideIDs: ['Verso_5a57825a4cfad13070870dc9'], + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4b_3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny.jpeg', + label: '3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny.jpeg' + }, + { + id: '5a5cc9594cfad17bed092f4c', + projectIDs: [ + '5a57825a4cfad13070870dc3' + ], + sideIDs: ['Verso_5a57825a4cfad13070870dcc'], + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4c_1_105.jpeg', + label: '1_105.jpeg' + }, + { + id: '5a5783154cfad13070870e0e', + projectIDs: [ + '5a57825a4cfad13070870dc3' + ], + sideIDs: [], + url: 'http://localhost:3001/images/5a5783154cfad13070870e0e_shiba_inu_taiki.jpeg', + label: 'shiba_inu_taiki.jpeg' + }, + { + id: '5a5cc95a4cfad17bed092f4e', + projectIDs: [ + '5a57825a4cfad13070870dc3' + ], + sideIDs: [], + url: 'http://localhost:3001/images/5a5cc95a4cfad17bed092f4e_cnrvtp6vaaamulm.png', + label: 'cnrvtp6vaaamulm.png' + }, + { + id: '5a5783154cfad13070870e13', + projectIDs: [ + '5a57825a4cfad13070870dc3' + ], + sideIDs: [], + url: 'http://localhost:3001/images/5a5783154cfad13070870e13_shiba_inu_3jpg.jpeg', + label: 'shiba_inu_3jpg.jpeg' + }, + { + id: '5a5783154cfad16535870e13', + projectIDs: [], + sideIDs: [], + url: 'http://localhost:3001/images/5a5783154cfad16535870e13_103496018.jpeg', + label: '103496018.jpeg' + } + ], + importStatus: null +} \ No newline at end of file diff --git a/viscoll-app/__test__/testData/membersStructure01.js b/viscoll-app/__test__/testData/membersStructure01.js new file mode 100644 index 00000000..aa39f4d3 --- /dev/null +++ b/viscoll-app/__test__/testData/membersStructure01.js @@ -0,0 +1,308 @@ +/* This has the following structure +Group 1 + Leaf 1 + Leaf 2 +Group 2 + Group 3 + Leaf 3 + Group 4 + Leaf 4 + Leaf 5 +*/ + + +export const side0_leaf1 = { + id: "side0_leaf1_id", + member_type: "Side", + leaf_id: "leaf1_id", + order: 0, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side1_leaf1 = { + id: "side1_leaf1_id", + member_type: "Side", + leaf_id: "leaf1_id", + order: 1, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side0_leaf2 = { + id: "side0_leaf2_id", + member_type: "Side", + leaf_id: "leaf2_id", + order: 0, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + + +export const side1_leaf2 = { + id: "side1_leaf2_id", + member_type: "Side", + leaf_id: "leaf2_id", + order: 1, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side0_leaf3 = { + id: "side0_leaf3_id", + member_type: "Side", + leaf_id: "leaf3_id", + order: 0, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side1_leaf3 = { + id: "side1_leaf3_id", + member_type: "Side", + leaf_id: "leaf3_id", + order: 1, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side0_leaf4 = { + id: "side0_leaf4_id", + member_type: "Side", + leaf_id: "leaf4_id", + order: 0, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side1_leaf4 = { + id: "side1_leaf4_id", + member_type: "Side", + leaf_id: "leaf4_id", + order: 1, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side0_leaf5 = { + id: "side0_leaf5_id", + member_type: "Side", + leaf_id: "leaf5_id", + order: 0, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + +export const side1_leaf5 = { + id: "side1_leaf5_id", + member_type: "Side", + leaf_id: "leaf5_id", + order: 1, + folio_number: "None", + texture: "None", + uri: "None", + script_direction: "None", + notes: [] +} + + +export const leaf1 = { + id: "leaf1_id", + member_type: "Leaf", + member_order: 1, + order: 1, + material: "Paper", + type: "None", + conjoined_to: "555", + attached_to: { + aboveID: "", + aboveMethod: "", + belowID: "", + belowMethod: "" + }, + stub: "None", + notes: [], + sides: [ + side0_leaf1, + side1_leaf1 + ] +} + +export const leaf2 = { + id: "leaf2_id", + member_type: "Leaf", + member_order: 2, + order: 2, + material: "Paper", + type: "None", + conjoined_to: "555", + attached_to: { + aboveID: "", + aboveMethod: "", + belowID: "", + belowMethod: "" + }, + stub: "None", + notes: [], + sides: [ + side0_leaf2, + side1_leaf2 + ] +} + +export const leaf3 = { + id: "leaf3_id", + member_type: "Leaf", + member_order: 1, + order: 3, + material: "None", + type: "None", + conjoined_to: "555", + attached_to: { + aboveID: "", + aboveMethod: "", + belowID: "", + belowMethod: "" + }, + stub: "None", + notes: [], + sides: [ + side0_leaf3, + side1_leaf3 + ] +} + +export const leaf4 = { + id: "leaf4_id", + member_type: "Leaf", + member_order: 1, + order: 4, + material: "None", + type: "None", + conjoined_to: "555", + attached_to: { + aboveID: "", + aboveMethod: "", + belowID: "", + belowMethod: "" + }, + stub: "None", + notes: [], + sides: [ + side0_leaf4, + side1_leaf4 + ] +} + +export const leaf5 = { + id: "leaf5_id", + member_type: "Leaf", + member_order: 2, + order: 5, + material: "None", + type: "None", + conjoined_to: "555", + attached_to: { + aboveID: "", + aboveMethod: "", + belowID: "", + belowMethod: "" + }, + stub: "None", + notes: [], + sides: [ + side0_leaf5, + side1_leaf5 + ] +} + +export const group1 = { + id: "group1_id", + member_type: "Group", + member_order: 1, + order: 1, + title: "Default", + type: "Quire", + notes: [], + members: [ + leaf1, + leaf2 + ] +} + + +export const group4 = { + id: "group4_id", + member_type: "Group", + member_order: 2, + order: 4, + title: "Default", + type: "Quire", + notes: [], + members: [ + leaf4 + ] +} + +export const group3 = { + id: "group3_id", + member_type: "Group", + member_order: 1, + order: 3, + title: "Default", + type: "Quire", + notes: [], + members: [ + leaf3, + group4 + ] +} + +export const group2 = { + id: "group2_id", + member_type: "Group", + member_order: 2, + order: 2, + title: "Default", + type: "Quire", + notes: [], + members: [ + group3, + leaf5 + ] +} + + + +export const members = [ + group1, + group2 +] + diff --git a/viscoll-app/__test__/testData/projectState001.js b/viscoll-app/__test__/testData/projectState001.js new file mode 100644 index 00000000..339a690f --- /dev/null +++ b/viscoll-app/__test__/testData/projectState001.js @@ -0,0 +1,4737 @@ +export const projectState001 = { + project: { + id: '5a57825a4cfad13070870dc3', + title: 'my prject', + shelfmark: 'mss 568', + metadata: { + date: '19th century' + }, + preferences: { + showTips: true + }, + noteTypes: [ + 'Unknown', + 'Ink', + 'Hand', + 'Damage' + ], + manifests: { + DIYImages: { + id: 'DIYImages', + images: [ + { + label: 'cguk1l0u4aeewdf.jpeg', + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4a_cguk1l0u4aeewdf.jpeg', + manifestID: 'DIYImages' + }, + { + label: '5a5cc9594cfad17bed092f4b_chiba_inu_dogs_shiba_inu_funny.jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e0a_5a5cc9594cfad17bed092f4b_chiba_inu_dogs_shiba_inu_funny.jpeg', + manifestID: 'DIYImages' + }, + { + label: '1_105.jpeg', + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4c_1_105.jpeg', + manifestID: 'DIYImages' + }, + { + label: 'shiba_inu_taiki.jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e0e_shiba_inu_taiki.jpeg', + manifestID: 'DIYImages' + }, + { + label: 'cnrvtp6vaaamulm.png', + url: 'http://localhost:3001/images/5a5cc95a4cfad17bed092f4e_cnrvtp6vaaamulm.png', + manifestID: 'DIYImages' + }, + { + label: 'shiba_inu_3jpg.jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e13_shiba_inu_3jpg.jpeg', + manifestID: 'DIYImages' + } + ], + name: 'Uploaded Images' + }, + '5a25b0703b0eb7478b415bd4': { + id: '5a25b0703b0eb7478b415bd4', + url: 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest', + images: [ + { + label: 'Hollar_a_3000_0001', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0002', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0003', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0004', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0004', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0005', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0006', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0006', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0007', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0008', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0008', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0009', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0010', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0010', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0011', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0012', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0012', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0013', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0013', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0014', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0014', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0015', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0015', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0016', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0016', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0017', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0017', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0018', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0018', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0019', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0019', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0020', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0020', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0021', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0021', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0022', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0022', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0023', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0023', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0024', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0024', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0025', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0025', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0026', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0026', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0027', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0027', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0028', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0028', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0029', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0029', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0030', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0030', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0031', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0031', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0032', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0032', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0033', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0033', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0034', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0034', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0035', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0035', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0036', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0036', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0037', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0037', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0038', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0038', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0039', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0039', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0040', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0040', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0041', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0041', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0042', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0042', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0043', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0043', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0044', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0044', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0045', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0045', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0046', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0046', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0047', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0047', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0048', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0048', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0049', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0049', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0050', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0050', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0051', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0051', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0052', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0052', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0053', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0053', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0054', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0054', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0055', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0055', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0056', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0056', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0057', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0057', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0058', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0058', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0059', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0059', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0060', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0060', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0061', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0061', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0062', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0062', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0063', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0063', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0064', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0064', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0065', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0065', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0066', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0066', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0067', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0067', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0068', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0068', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0069', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0069', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0070', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0070', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0071', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0071', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0072', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0072', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0073', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0073', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0074', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0074', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0075', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0075', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0076', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0076', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0077', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0077', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0078', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0078', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0079', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0079', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0080', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0080', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0081', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0081', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0082', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0082', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0083', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0083', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0084', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0084', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0085', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0085', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0086', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0086', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0087', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0087', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0088', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0088', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0089', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0089', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0090', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0090', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0091', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0091', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0092', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0092', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0093', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0093', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0094', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0094', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0095', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0095', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0096', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0096', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0097', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0097', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0098', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0098', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0099', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0099', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0100', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0100', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0101', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0101', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0102', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0102', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0103', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0103', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0104', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0104', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0105', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0105', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0106', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0106', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0107', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0107', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0108', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0108', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0109', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0109', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0110', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0110', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0111', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0111', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0112', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0112', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0113', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0113', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0114', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0114', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0115', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0115', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0116', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0116', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0117', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0117', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0118', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0118', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0119', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0119', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0120', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0120', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0121', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0121', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0122', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0122', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0123', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0123', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0124', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0124', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0125', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0125', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0126', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0126', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0127', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0127', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0128', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0128', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0129', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0129', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0130', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0130', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0131', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0131', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0132', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0132', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0133', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0133', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0134', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0134', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0135', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0135', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0136', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0136', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0137', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0137', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0138', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0138', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0139', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0139', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0140', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0140', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0141', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0141', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0142', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0142', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0143', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0143', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0144', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0144', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0145', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0145', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0146', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0146', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0147', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0147', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0148', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0148', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0149', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0149', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0150', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0150', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0151', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0151', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0152', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0152', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0153', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0153', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0154', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0154', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0155', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0155', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0156', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0156', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0157', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0157', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0158', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0158', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0159', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0159', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0160', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0160', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0161', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0161', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0162', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0162', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0163', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0163', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0164', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0164', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0165', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0165', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0166', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0166', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0167', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0167', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0168', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0168', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0169', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0169', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0170', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0170', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0171', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0171', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0172', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0172', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0173', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0173', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0174', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0174', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0175', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0175', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0176', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0176', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0177', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0177', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0178', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0178', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0179', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0179', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0180', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0180', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0181', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0181', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0182', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0182', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0183', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0183', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0184', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0184', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0185', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0185', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0186', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0186', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0187', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0187', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0188', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0188', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0189', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0189', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0190', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0190', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0191', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0191', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0192', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0192', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0193', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0193', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0194', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0194', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0195', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0195', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0196', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0196', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0197', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0197', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0198', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0198', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0199', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0199', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0200', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0200', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0201', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0201', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0202', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0202', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0203', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0203', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0204', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0204', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0205', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0205', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0206', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0206', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0207', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0207', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0208', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0208', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0209', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0209', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0210', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0210', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0211', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0211', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0212', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0212', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0213', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0213', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0214', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0214', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0215', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0215', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0216', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0216', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0217', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0217', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0218', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0218', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0219', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0219', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0220', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0220', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0221', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0221', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0222', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0222', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0223', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0223', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0224', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0224', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0225', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0225', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0226', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0226', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0227', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0227', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0228', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0228', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0229', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0229', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0230', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0230', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0231', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0231', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0232', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0232', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0233', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0233', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0234', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0234', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0235', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0235', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0236', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0236', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0237', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0237', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0238', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0238', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0239', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0239', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0240', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0240', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0241', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0241', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0242', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0242', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0243', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0243', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0244', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0244', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0245', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0245', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0246', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0246', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0247', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0247', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0248', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0248', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0249', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0249', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0250', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0250', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0251', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0251', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0252', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0252', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0253', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0253', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0254', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0254', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0255', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0255', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0256', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0256', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0257', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0257', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0258', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0258', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0259', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0259', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0260', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0260', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0261', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0261', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0262', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0262', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0263', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0263', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0264', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0264', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0265', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0265', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0266', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0266', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0267', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0267', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0268', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0268', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0269', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0269', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0270', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0270', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0271', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0271', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0272', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0272', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0273', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0273', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0274', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0274', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0275', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0275', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0276', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0276', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0277', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0277', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0278', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0278', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0279', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0279', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0280', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0280', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0281', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0281', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0282', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0282', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0283', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0283', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0284', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0284', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0285', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0285', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0286', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0286', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0287', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0287', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0288', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0288', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0289', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0289', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0290', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0290', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0291', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0291', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0292', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0292', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0293', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0293', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0294', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0294', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0295', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0295', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0296', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0296', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0297', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0297', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0298', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0298', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0299', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0299', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0300', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0300', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0301', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0301', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0302', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0302', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0303', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0303', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0304', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0304', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0305', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0305', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0306', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0306', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0307', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0307', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0308', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0308', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0309', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0309', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0310', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0310', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0311', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0311', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0312', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0312', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0313', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0313', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0314', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0314', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0315', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0315', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0316', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0316', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0317', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0317', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0318', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0318', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0319', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0319', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0320', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0320', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0321', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0321', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0322', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0322', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0323', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0323', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0324', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0324', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0325', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0325', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0326', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0326', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0327', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0327', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0328', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0328', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0329', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0329', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0330', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0330', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0331', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0331', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0332', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0332', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0333', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0333', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0334', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0334', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0335', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0335', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0336', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0336', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0337', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0337', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0338', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0338', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0339', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0339', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0340', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0340', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0341', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0341', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0342', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0342', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0343', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0343', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0344', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0344', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0345', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0345', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0346', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0346', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0347', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0347', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0348', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0348', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0349', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0349', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0350', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0350', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0351', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0351', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0352', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0352', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0353', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0353', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0354', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0354', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0355', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0355', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0356', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0356', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0357', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0357', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0358', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0358', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0359', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0359', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0360', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0360', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0361', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0361', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0362', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0362', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0363', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0363', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0364', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0364', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0365', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0365', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0366', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0366', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0367', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0367', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0368', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0368', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0369', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0369', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0370', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0370', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0371', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0371', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0372', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0372', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0373', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0373', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0374', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0374', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0375', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0375', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0376', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0376', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0377', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0377', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0378', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0378', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0379', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0379', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0380', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0380', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0381', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0381', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0382', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0382', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0383', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0383', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0384', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0384', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0385', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0385', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0386', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0386', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0387', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0387', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0388', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0388', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0389', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0389', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0390', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0390', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0391', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0391', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0392', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0392', + manifestID: '5a25b0703b0eb7478b415bd4' + } + ], + name: 'The fables of Aesop / paraphras\'d in verse, and...' + }, + '5a25b0763b0eb7478b415bd5': { + id: '5a25b0763b0eb7478b415bd5', + url: 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3001/manifest', + images: [ + { + label: 'Hollar_a_3001_0001', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0001', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0002', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0002', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0003', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0003', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0004', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0004', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0005', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0005', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0006', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0006', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0007', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0007', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0008', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0008', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0009', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0009', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0010', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0010', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0011', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0011', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0012', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0012', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0013', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0013', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0014', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0014', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0015', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0015', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0016', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0016', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0017', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0017', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0018', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0018', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0019', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0019', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0020', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0020', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0021', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0021', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0022', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0022', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0023', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0023', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0024', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0024', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0025', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0025', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0026', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0026', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0027', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0027', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0028', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0028', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0029', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0029', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0030', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0030', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0031', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0031', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0032', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0032', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0033', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0033', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0034', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0034', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0035', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0035', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0036', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0036', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0037', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0037', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0038', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0038', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0039', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0039', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0040', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0040', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0041', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0041', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0042', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0042', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0043', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0043', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0044', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0044', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0045', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0045', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0046', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0046', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0047', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0047', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0048', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0048', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0049', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0049', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0050', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0050', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0051', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0051', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0052', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0052', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0053', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0053', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0054', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0054', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0055', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0055', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0056', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0056', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0057', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0057', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0058', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0058', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0059', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0059', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0060', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0060', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0061', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0061', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0062', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0062', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0063', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0063', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0064', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0064', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0065', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0065', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0066', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0066', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0067', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0067', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0068', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0068', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0069', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0069', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0070', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0070', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0071', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0071', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0072', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0072', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0073', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0073', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0074', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0074', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0075', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0075', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0076', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0076', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0077', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0077', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0078', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0078', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0079', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0079', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0080', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0080', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0081', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0081', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0082', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0082', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0083', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0083', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0084', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0084', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0085', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0085', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0086', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0086', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0087', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0087', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0088', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0088', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0089', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0089', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0090', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0090', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0091', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0091', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0092', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0092', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0093', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0093', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0094', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0094', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0095', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0095', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0096', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0096', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0097', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0097', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0098', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0098', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0099', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0099', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0100', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0100', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0101', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0101', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0102', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0102', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0103', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0103', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0104', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0104', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0105', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0105', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0106', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0106', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0107', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0107', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0108', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0108', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0109', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0109', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0110', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0110', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0111', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0111', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0112', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0112', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0113', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0113', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0114', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0114', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0115', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0115', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0116', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0116', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0117', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0117', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0118', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0118', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0119', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0119', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0120', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0120', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0121', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0121', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0122', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0122', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0123', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0123', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0124', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0124', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0125', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0125', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0126', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0126', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0127', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0127', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0128', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0128', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0129', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0129', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0130', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0130', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0131', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0131', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0132', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0132', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0133', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0133', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0134', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0134', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0135', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0135', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0136', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0136', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0137', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0137', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0138', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0138', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0139', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0139', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0140', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0140', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0141', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0141', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0142', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0142', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0143', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0143', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0144', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0144', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0145', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0145', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0146', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0146', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0147', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0147', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0148', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0148', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0149', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0149', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0150', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0150', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0151', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0151', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0152', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0152', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0153', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0153', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0154', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0154', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0155', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0155', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0156', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0156', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0157', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0157', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0158', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0158', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0159', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0159', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0160', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0160', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0161', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0161', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0162', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0162', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0163', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0163', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0164', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0164', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0165', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0165', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0166', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0166', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0167', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0167', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0168', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0168', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0169', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0169', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0170', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0170', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0171', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0171', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0172', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0172', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0173', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0173', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0174', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0174', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0175', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0175', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0176', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0176', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0177', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0177', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0178', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0178', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0179', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0179', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0180', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0180', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0181', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0181', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0182', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0182', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0183', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0183', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0184', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0184', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0185', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0185', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0186', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0186', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0187', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0187', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0188', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0188', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0189', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0189', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0190', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0190', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0191', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0191', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0192', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0192', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0193', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0193', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0194', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0194', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0195', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0195', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0196', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0196', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0197', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0197', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0198', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0198', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0199', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0199', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0200', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0200', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0201', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0201', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0202', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0202', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0203', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0203', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0204', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0204', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0205', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0205', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0206', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0206', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0207', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0207', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0208', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0208', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0209', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0209', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0210', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0210', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0211', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0211', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0212', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0212', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0213', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0213', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0214', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0214', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0215', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0215', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0216', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0216', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0217', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0217', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0218', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0218', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0219', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0219', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0220', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0220', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0221', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0221', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0222', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0222', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0223', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0223', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0224', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0224', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0225', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0225', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0226', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0226', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0227', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0227', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0228', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0228', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0229', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0229', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0230', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0230', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0231', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0231', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0232', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0232', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0233', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0233', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0234', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0234', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0235', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0235', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0236', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0236', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0237', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0237', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0238', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0238', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0239', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0239', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0240', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0240', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0241', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0241', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0242', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0242', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0243', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0243', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0244', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0244', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0245', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0245', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0246', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0246', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0247', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0247', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0248', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0248', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0249', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0249', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0250', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0250', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0251', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0251', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0252', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0252', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0253', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0253', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0254', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0254', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0255', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0255', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0256', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0256', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0257', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0257', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0258', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0258', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0259', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0259', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0260', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0260', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0261', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0261', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0262', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0262', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0263', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0263', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0264', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0264', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0265', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0265', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0266', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0266', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0267', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0267', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0268', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0268', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0269', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0269', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0270', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0270', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0271', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0271', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0272', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0272', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0273', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0273', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0274', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0274', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0275', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0275', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0276', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0276', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0277', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0277', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0278', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0278', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0279', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0279', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0280', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0280', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0281', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0281', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0282', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0282', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0283', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0283', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0284', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0284', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0285', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0285', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0286', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0286', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0287', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0287', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0288', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0288', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0289', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0289', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0290', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0290', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0291', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0291', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0292', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0292', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0293', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0293', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0294', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0294', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0295', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0295', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0296', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0296', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0297', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0297', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0298', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0298', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0299', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0299', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0300', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0300', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0301', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0301', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0302', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0302', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0303', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0303', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0304', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0304', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0305', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0305', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0306', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0306', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0307', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0307', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0308', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0308', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0309', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0309', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0310', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0310', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0311', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0311', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0312', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0312', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0313', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0313', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0314', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0314', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0315', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0315', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0316', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0316', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0317', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0317', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0318', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0318', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0319', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0319', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0320', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0320', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0321', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0321', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0322', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0322', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0323', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0323', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0324', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0324', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0325', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0325', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0326', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0326', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0327', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0327', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0328', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0328', + manifestID: '5a25b0763b0eb7478b415bd5' + } + ], + name: 'The history of St. Paul\'s Cathedral in London :...' + } + }, + groupIDs: [ + 'Group_5a57825a4cfad13070870df4', + 'Group_5a57825a4cfad13070870df5', + 'Group_5a57825a4cfad13070870df6', + 'Group_5a57825a4cfad13070870df7' + ], + leafIDs: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dca', + 'Leaf_5a57825a4cfad13070870dcd', + 'Leaf_5a57825a4cfad13070870dd0', + 'Leaf_5a57825a4cfad13070870dd3', + 'Leaf_5a57825a4cfad13070870dd6', + 'Leaf_5a57825a4cfad13070870dd9', + 'Leaf_5a57825a4cfad13070870ddc', + 'Leaf_5a57825a4cfad13070870ddf', + 'Leaf_5a57825a4cfad13070870de2', + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb', + 'Leaf_5a57825a4cfad13070870dee', + 'Leaf_5a57825a4cfad13070870df1' + ], + rectoIDs: [ + 'Recto_5a57825a4cfad13070870dc5', + 'Recto_5a57825a4cfad13070870dc8', + 'Recto_5a57825a4cfad13070870dcb', + 'Recto_5a57825a4cfad13070870dce', + 'Recto_5a57825a4cfad13070870dd1', + 'Recto_5a57825a4cfad13070870dd4', + 'Recto_5a57825a4cfad13070870dd7', + 'Recto_5a57825a4cfad13070870dda', + 'Recto_5a57825a4cfad13070870ddd', + 'Recto_5a57825a4cfad13070870de0', + 'Recto_5a57825a4cfad13070870de3', + 'Recto_5a57825a4cfad13070870de6', + 'Recto_5a57825a4cfad13070870de9', + 'Recto_5a57825a4cfad13070870dec', + 'Recto_5a57825a4cfad13070870def', + 'Recto_5a57825a4cfad13070870df2' + ], + versoIDs: [ + 'Verso_5a57825a4cfad13070870dc6', + 'Verso_5a57825a4cfad13070870dc9', + 'Verso_5a57825a4cfad13070870dcc', + 'Verso_5a57825a4cfad13070870dcf', + 'Verso_5a57825a4cfad13070870dd2', + 'Verso_5a57825a4cfad13070870dd5', + 'Verso_5a57825a4cfad13070870dd8', + 'Verso_5a57825a4cfad13070870ddb', + 'Verso_5a57825a4cfad13070870dde', + 'Verso_5a57825a4cfad13070870de1', + 'Verso_5a57825a4cfad13070870de4', + 'Verso_5a57825a4cfad13070870de7', + 'Verso_5a57825a4cfad13070870dea', + 'Verso_5a57825a4cfad13070870ded', + 'Verso_5a57825a4cfad13070870df0', + 'Verso_5a57825a4cfad13070870df3' + ], + Groups: { + Group_5a57825a4cfad13070870df4: { + id: 'Group_5a57825a4cfad13070870df4', + type: 'Quire', + title: 'First Quire', + tacketed: [], + sewing: [ + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dcd' + ], + nestLevel: 1, + parentID: null, + notes: [ + '5a57825a4cfad13070870df8' + ], + memberIDs: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dca', + 'Leaf_5a57825a4cfad13070870dcd', + 'Leaf_5a57825a4cfad13070870dd0', + 'Leaf_5a57825a4cfad13070870dd3' + ], + memberType: 'Group' + }, + Group_5a57825a4cfad13070870df5: { + id: 'Group_5a57825a4cfad13070870df5', + type: 'Quire', + title: '2nd Quire', + tacketed: [ + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870dee' + ], + sewing: [], + nestLevel: 1, + parentID: null, + notes: [ + '5a57825a4cfad13070870df8' + ], + memberIDs: [ + 'Group_5a57825a4cfad13070870df6', + 'Leaf_5a57825a4cfad13070870de2', + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb', + 'Leaf_5a57825a4cfad13070870dee', + 'Leaf_5a57825a4cfad13070870df1' + ], + memberType: 'Group' + }, + Group_5a57825a4cfad13070870df6: { + id: 'Group_5a57825a4cfad13070870df6', + type: 'Quire', + title: '1st Sub Quire of 2', + tacketed: [], + sewing: [], + nestLevel: 2, + parentID: 'Group_5a57825a4cfad13070870df5', + notes: [], + memberIDs: [ + 'Group_5a57825a4cfad13070870df7', + 'Leaf_5a57825a4cfad13070870ddc', + 'Leaf_5a57825a4cfad13070870ddf' + ], + memberType: 'Group' + }, + Group_5a57825a4cfad13070870df7: { + id: 'Group_5a57825a4cfad13070870df7', + type: 'Quire', + title: '1st Sub Quire of Sub Quire 2.1', + tacketed: [], + sewing: [], + nestLevel: 3, + parentID: 'Group_5a57825a4cfad13070870df6', + notes: [], + memberIDs: [ + 'Leaf_5a57825a4cfad13070870dd6', + 'Leaf_5a57825a4cfad13070870dd9' + ], + memberType: 'Group' + } + }, + Leafs: { + Leaf_5a57825a4cfad13070870dc4: { + id: 'Leaf_5a57825a4cfad13070870dc4', + material: 'None', + type: 'Endleaf', + conjoined_to: 'Leaf_5a57825a4cfad13070870dd3', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dc5', + versoID: 'Verso_5a57825a4cfad13070870dc6', + notes: [ + '5a57825a4cfad13070870df9' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dc7: { + id: 'Leaf_5a57825a4cfad13070870dc7', + material: 'None', + type: 'Missing', + conjoined_to: 'Leaf_5a57825a4cfad13070870dd0', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dc8', + versoID: 'Verso_5a57825a4cfad13070870dc9', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dca: { + id: 'Leaf_5a57825a4cfad13070870dca', + material: 'Parchment', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dcd', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dcb', + versoID: 'Verso_5a57825a4cfad13070870dcc', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dcd: { + id: 'Leaf_5a57825a4cfad13070870dcd', + material: 'Parchment', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dca', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dce', + versoID: 'Verso_5a57825a4cfad13070870dcf', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd0: { + id: 'Leaf_5a57825a4cfad13070870dd0', + material: 'Parchment', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dc7', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dd1', + versoID: 'Verso_5a57825a4cfad13070870dd2', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd3: { + id: 'Leaf_5a57825a4cfad13070870dd3', + material: 'None', + type: 'Endleaf', + conjoined_to: 'Leaf_5a57825a4cfad13070870dc4', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dd4', + versoID: 'Verso_5a57825a4cfad13070870dd5', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd6: { + id: 'Leaf_5a57825a4cfad13070870dd6', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'None', + attached_below: 'Glued (Partial)', + stub: 'None', + nestLevel: 3, + parentID: 'Group_5a57825a4cfad13070870df7', + rectoID: 'Recto_5a57825a4cfad13070870dd7', + versoID: 'Verso_5a57825a4cfad13070870dd8', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd9: { + id: 'Leaf_5a57825a4cfad13070870dd9', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'Glued (Partial)', + attached_below: 'None', + stub: 'None', + nestLevel: 3, + parentID: 'Group_5a57825a4cfad13070870df7', + rectoID: 'Recto_5a57825a4cfad13070870dda', + versoID: 'Verso_5a57825a4cfad13070870ddb', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870ddc: { + id: 'Leaf_5a57825a4cfad13070870ddc', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870ddf', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 2, + parentID: 'Group_5a57825a4cfad13070870df6', + rectoID: 'Recto_5a57825a4cfad13070870ddd', + versoID: 'Verso_5a57825a4cfad13070870dde', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870ddf: { + id: 'Leaf_5a57825a4cfad13070870ddf', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870ddc', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 2, + parentID: 'Group_5a57825a4cfad13070870df6', + rectoID: 'Recto_5a57825a4cfad13070870de0', + versoID: 'Verso_5a57825a4cfad13070870de1', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870de2: { + id: 'Leaf_5a57825a4cfad13070870de2', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870df1', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870de3', + versoID: 'Verso_5a57825a4cfad13070870de4', + notes: [ + '5a57825a4cfad13070870df9' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870de5: { + id: 'Leaf_5a57825a4cfad13070870de5', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dee', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870de6', + versoID: 'Verso_5a57825a4cfad13070870de7', + notes: [ + '5a57825a4cfad13070870df8' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870de8: { + id: 'Leaf_5a57825a4cfad13070870de8', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'None', + attached_below: 'None', + stub: 'Original', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870de9', + versoID: 'Verso_5a57825a4cfad13070870dea', + notes: [ + '5a57825a4cfad13070870df8' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870deb: { + id: 'Leaf_5a57825a4cfad13070870deb', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870dec', + versoID: 'Verso_5a57825a4cfad13070870ded', + notes: [ + '5a57825a4cfad13070870df8' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dee: { + id: 'Leaf_5a57825a4cfad13070870dee', + material: 'None', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870de5', + attached_above: 'None', + attached_below: 'Glued (Complete)', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870def', + versoID: 'Verso_5a57825a4cfad13070870df0', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870df1: { + id: 'Leaf_5a57825a4cfad13070870df1', + material: 'None', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870de2', + attached_above: 'Glued (Complete)', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870df2', + versoID: 'Verso_5a57825a4cfad13070870df3', + notes: [], + memberType: 'Leaf' + } + }, + Rectos: { + Recto_5a57825a4cfad13070870dc5: { + id: 'Recto_5a57825a4cfad13070870dc5', + parentID: 'Leaf_5a57825a4cfad13070870dc4', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0003', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003' + }, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dc8: { + id: 'Recto_5a57825a4cfad13070870dc8', + parentID: 'Leaf_5a57825a4cfad13070870dc7', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0002', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002' + }, + script_direction: 'None', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dcb: { + id: 'Recto_5a57825a4cfad13070870dcb', + parentID: 'Leaf_5a57825a4cfad13070870dca', + folio_number: "custom XR", + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0001', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001' + }, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dce: { + id: 'Recto_5a57825a4cfad13070870dce', + parentID: 'Leaf_5a57825a4cfad13070870dcd', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0007', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007' + }, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dd1: { + id: 'Recto_5a57825a4cfad13070870dd1', + parentID: 'Leaf_5a57825a4cfad13070870dd0', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0009', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009' + }, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dd4: { + id: 'Recto_5a57825a4cfad13070870dd4', + parentID: 'Leaf_5a57825a4cfad13070870dd3', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0011', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011' + }, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dd7: { + id: 'Recto_5a57825a4cfad13070870dd7', + parentID: 'Leaf_5a57825a4cfad13070870dd6', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dda: { + id: 'Recto_5a57825a4cfad13070870dda', + parentID: 'Leaf_5a57825a4cfad13070870dd9', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870ddd: { + id: 'Recto_5a57825a4cfad13070870ddd', + parentID: 'Leaf_5a57825a4cfad13070870ddc', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de0: { + id: 'Recto_5a57825a4cfad13070870de0', + parentID: 'Leaf_5a57825a4cfad13070870ddf', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de3: { + id: 'Recto_5a57825a4cfad13070870de3', + parentID: 'Leaf_5a57825a4cfad13070870de2', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de6: { + id: 'Recto_5a57825a4cfad13070870de6', + parentID: 'Leaf_5a57825a4cfad13070870de5', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de9: { + id: 'Recto_5a57825a4cfad13070870de9', + parentID: 'Leaf_5a57825a4cfad13070870de8', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dec: { + id: 'Recto_5a57825a4cfad13070870dec', + parentID: 'Leaf_5a57825a4cfad13070870deb', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870def: { + id: 'Recto_5a57825a4cfad13070870def', + parentID: 'Leaf_5a57825a4cfad13070870dee', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870df2: { + id: 'Recto_5a57825a4cfad13070870df2', + parentID: 'Leaf_5a57825a4cfad13070870df1', + folio_number: null, + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + } + }, + Versos: { + Verso_5a57825a4cfad13070870dc6: { + id: 'Verso_5a57825a4cfad13070870dc6', + parentID: 'Leaf_5a57825a4cfad13070870dc4', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: { + manifestID: 'DIYImages', + label: 'cguk1l0u4aeewdf.jpeg', + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4a_cguk1l0u4aeewdf.jpeg' + }, + script_direction: 'None', + notes: [ + '5a57825a4cfad13070870df9', + '5a57825a4cfad13070870dfa' + ], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dc9: { + id: 'Verso_5a57825a4cfad13070870dc9', + parentID: 'Leaf_5a57825a4cfad13070870dc7', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: { + manifestID: 'DIYImages', + label: '5a5cc9594cfad17bed092f4b_chiba_inu_dogs_shiba_inu_funny.jpeg', + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4b_3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny.jpeg' + }, + script_direction: 'None', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dcc: { + id: 'Verso_5a57825a4cfad13070870dcc', + parentID: 'Leaf_5a57825a4cfad13070870dca', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: { + manifestID: 'DIYImages', + label: '1_105.jpeg', + url: 'http://localhost:3001/images/5a5cc9594cfad17bed092f4c_1_105.jpeg' + }, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dcf: { + id: 'Verso_5a57825a4cfad13070870dcf', + parentID: 'Leaf_5a57825a4cfad13070870dcd', + folio_number: "custom XV", + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dd2: { + id: 'Verso_5a57825a4cfad13070870dd2', + parentID: 'Leaf_5a57825a4cfad13070870dd0', + folio_number: "custom YV", + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dd5: { + id: 'Verso_5a57825a4cfad13070870dd5', + parentID: 'Leaf_5a57825a4cfad13070870dd3', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dd8: { + id: 'Verso_5a57825a4cfad13070870dd8', + parentID: 'Leaf_5a57825a4cfad13070870dd6', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870ddb: { + id: 'Verso_5a57825a4cfad13070870ddb', + parentID: 'Leaf_5a57825a4cfad13070870dd9', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dde: { + id: 'Verso_5a57825a4cfad13070870dde', + parentID: 'Leaf_5a57825a4cfad13070870ddc', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870de1: { + id: 'Verso_5a57825a4cfad13070870de1', + parentID: 'Leaf_5a57825a4cfad13070870ddf', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870de4: { + id: 'Verso_5a57825a4cfad13070870de4', + parentID: 'Leaf_5a57825a4cfad13070870de2', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870de7: { + id: 'Verso_5a57825a4cfad13070870de7', + parentID: 'Leaf_5a57825a4cfad13070870de5', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dea: { + id: 'Verso_5a57825a4cfad13070870dea', + parentID: 'Leaf_5a57825a4cfad13070870de8', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870ded: { + id: 'Verso_5a57825a4cfad13070870ded', + parentID: 'Leaf_5a57825a4cfad13070870deb', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'Right-To-Left', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870df0: { + id: 'Verso_5a57825a4cfad13070870df0', + parentID: 'Leaf_5a57825a4cfad13070870dee', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'Right-To-Left', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870df3: { + id: 'Verso_5a57825a4cfad13070870df3', + parentID: 'Leaf_5a57825a4cfad13070870df1', + folio_number: null, + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'Right-To-Left', + notes: [], + memberType: 'Verso' + } + }, + Notes: { + '5a57825a4cfad13070870df8': { + id: '5a57825a4cfad13070870df8', + title: 'Black ink', + type: 'Ink', + description: 'Some black ink over here\n', + show: true, + objects: { + Group: [ + 'Group_5a57825a4cfad13070870df4', + 'Group_5a57825a4cfad13070870df5' + ], + Leaf: [ + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb' + ], + Recto: [], + Verso: [] + } + }, + '5a57825a4cfad13070870df9': { + id: '5a57825a4cfad13070870df9', + title: 'John\'s hand', + type: 'Hand', + description: 'Look ! ', + show: false, + objects: { + Group: [], + Leaf: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870de2' + ], + Recto: [], + Verso: [ + 'Verso_5a57825a4cfad13070870dc6' + ] + } + }, + '5a57825a4cfad13070870dfa': { + id: '5a57825a4cfad13070870dfa', + title: 'Fire', + type: 'Damage', + description: 'Some burnt marks', + show: true, + objects: { + Group: [], + Leaf: [ + 'Leaf_5a57825a4cfad13070870dca', + 'Leaf_5a57825a4cfad13070870dcd', + 'Leaf_5a57825a4cfad13070870dd0' + ], + Recto: [ + 'Recto_5a57825a4cfad13070870dc8' + ], + Verso: [ + 'Verso_5a57825a4cfad13070870dc6', + 'Verso_5a57825a4cfad13070870dc9' + ] + } + } + } + }, + managerMode: 'collationManager', + collationManager: { + selectedObjects: { + type: 'Leaf', + members: [ + 'Leaf_5a57825a4cfad13070870dc4' + ], + lastSelected: 'Leaf_5a57825a4cfad13070870dc4' + }, + viewMode: 'VISUAL', + visibleAttributes: { + group: { + type: false, + title: false + }, + leaf: { + type: false, + material: false, + conjoined_leaf_order: false, + attached_below: false, + attached_above: false, + stub: false + }, + side: { + folio_number: false, + texture: false, + script_direction: false, + uri: false + } + }, + defaultAttributes: { + leaf: [ + { + name: 'type', + displayName: 'Type', + options: [ + 'None', + 'Original', + 'Added', + 'Missing', + 'Hook', + 'Endleaf', + 'Replaced' + ], + isDropdown: true + }, + { + name: 'material', + displayName: 'Material', + options: [ + 'None', + 'Parchment', + 'Paper', + 'Other' + ], + isDropdown: true + }, + { + name: 'conjoined_to', + displayName: 'Conjoined To', + isDropdown: true + }, + { + name: 'attached_above', + displayName: 'Attached Above', + options: [ + 'None', + 'Glued (Partial)', + 'Glued (Complete)', + 'Glued (Drumming)', + 'Other' + ], + isDropdown: true + }, + { + name: 'attached_below', + displayName: 'Attached Below', + options: [ + 'None', + 'Glued (Partial)', + 'Glued (Complete)', + 'Glued (Drumming)', + 'Other' + ], + isDropdown: true + }, + { + name: 'stub', + displayName: 'Stub', + options: [ + 'None', + 'Original', + 'Added' + ], + isDropdown: true + } + ], + group: [ + { + name: 'type', + displayName: 'Type', + options: [ + 'Quire', + 'Booklet' + ], + isDropdown: true + }, + { + name: 'title', + displayName: 'Title' + } + ], + side: [ + { + name: 'texture', + displayName: 'Texture', + options: [ + 'None', + 'Hair', + 'Flesh', + 'Felt', + 'Wire' + ], + isDropdown: true + }, + { + name: 'folio_number', + displayName: 'Folio Number' + }, + { + name: 'script_direction', + displayName: 'Script Direction', + options: [ + 'None', + 'Left-to-Right', + 'Right-To-Left', + 'Top-To-Bottom' + ], + isDropdown: true + }, + { + name: 'uri', + displayName: 'URI' + } + ], + note: [ + { + name: 'title', + displayName: 'Title' + }, + { + name: 'type', + displayName: 'Type', + isDropdown: true + }, + { + name: 'description', + displayName: 'Description' + } + ] + }, + filters: { + filterPanelOpen: false, + Groups: [], + Leafs: [], + Sides: [], + Notes: [], + GroupsOfMatchingLeafs: [], + LeafsOfMatchingSides: [], + GroupsOfMatchingSides: [], + GroupsOfMatchingNotes: [], + LeafsOfMatchingNotes: [], + SidesOfMatchingNotes: [], + active: false, + hideOthers: false, + queries: [ + { + type: null, + attribute: '', + attributeIndex: '', + values: [], + condition: '', + conjunction: '' + } + ], + selection: '' + }, + flashItems: { + leaves: [], + groups: [] + }, + visualizations: { + tacketed: '', + sewing: '' + } + }, + notesManager: { + activeTab: 'MANAGE' + }, + imageManager: { + activeTab: 'MANAGE', + manageSources: { + error: '' + } + }, +} \ No newline at end of file diff --git a/viscoll-app/__test__/testData/state001.js b/viscoll-app/__test__/testData/state001.js new file mode 100644 index 00000000..890a1eb0 --- /dev/null +++ b/viscoll-app/__test__/testData/state001.js @@ -0,0 +1,4737 @@ +export const state001 = { + project: { + id: '5a57825a4cfad13070870dc3', + title: 'my prject', + shelfmark: 'mss 568', + metadata: { + date: '19th century' + }, + preferences: { + showTips: true + }, + noteTypes: [ + 'Unknown', + 'Ink', + 'Hand', + 'Damage' + ], + manifests: { + DIYImages: { + id: 'DIYImages', + images: [ + { + label: 'cguk1l0u4aeewdf.jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e08_cguk1l0u4aeewdf.jpeg', + manifestID: 'DIYImages' + }, + { + label: '3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny_copy(1).jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e0a_3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny_copy(1).jpeg', + manifestID: 'DIYImages' + }, + { + label: '1_105_copy(1).jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e0c_1_105_copy(1).jpeg', + manifestID: 'DIYImages' + }, + { + label: 'shiba_inu_taiki_copy(1).jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e0e_shiba_inu_taiki_copy(1).jpeg', + manifestID: 'DIYImages' + }, + { + label: 'cnrvtp6vaaamulm_copy(2).png', + url: 'http://localhost:3001/images/5a5783154cfad13070870e11_cnrvtp6vaaamulm_copy(2).png', + manifestID: 'DIYImages' + }, + { + label: 'shiba_inu_3jpg_copy(1).jpeg', + url: 'http://localhost:3001/images/5a5783154cfad13070870e13_shiba_inu_3jpg_copy(1).jpeg', + manifestID: 'DIYImages' + } + ], + name: 'Uploaded Images' + }, + '5a25b0703b0eb7478b415bd4': { + id: '5a25b0703b0eb7478b415bd4', + url: 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3000/manifest', + images: [ + { + label: 'Hollar_a_3000_0001', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0002', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0003', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0004', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0004', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0005', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0005', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0006', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0006', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0007', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0008', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0008', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0009', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0010', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0010', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0011', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0012', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0012', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0013', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0013', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0014', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0014', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0015', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0015', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0016', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0016', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0017', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0017', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0018', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0018', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0019', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0019', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0020', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0020', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0021', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0021', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0022', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0022', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0023', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0023', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0024', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0024', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0025', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0025', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0026', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0026', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0027', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0027', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0028', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0028', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0029', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0029', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0030', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0030', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0031', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0031', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0032', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0032', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0033', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0033', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0034', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0034', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0035', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0035', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0036', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0036', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0037', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0037', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0038', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0038', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0039', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0039', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0040', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0040', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0041', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0041', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0042', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0042', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0043', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0043', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0044', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0044', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0045', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0045', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0046', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0046', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0047', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0047', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0048', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0048', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0049', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0049', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0050', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0050', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0051', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0051', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0052', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0052', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0053', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0053', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0054', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0054', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0055', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0055', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0056', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0056', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0057', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0057', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0058', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0058', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0059', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0059', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0060', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0060', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0061', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0061', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0062', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0062', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0063', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0063', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0064', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0064', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0065', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0065', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0066', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0066', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0067', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0067', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0068', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0068', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0069', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0069', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0070', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0070', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0071', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0071', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0072', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0072', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0073', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0073', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0074', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0074', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0075', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0075', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0076', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0076', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0077', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0077', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0078', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0078', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0079', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0079', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0080', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0080', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0081', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0081', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0082', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0082', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0083', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0083', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0084', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0084', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0085', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0085', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0086', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0086', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0087', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0087', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0088', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0088', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0089', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0089', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0090', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0090', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0091', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0091', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0092', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0092', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0093', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0093', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0094', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0094', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0095', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0095', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0096', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0096', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0097', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0097', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0098', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0098', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0099', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0099', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0100', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0100', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0101', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0101', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0102', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0102', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0103', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0103', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0104', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0104', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0105', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0105', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0106', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0106', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0107', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0107', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0108', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0108', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0109', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0109', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0110', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0110', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0111', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0111', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0112', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0112', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0113', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0113', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0114', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0114', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0115', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0115', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0116', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0116', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0117', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0117', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0118', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0118', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0119', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0119', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0120', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0120', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0121', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0121', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0122', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0122', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0123', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0123', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0124', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0124', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0125', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0125', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0126', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0126', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0127', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0127', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0128', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0128', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0129', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0129', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0130', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0130', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0131', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0131', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0132', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0132', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0133', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0133', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0134', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0134', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0135', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0135', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0136', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0136', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0137', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0137', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0138', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0138', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0139', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0139', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0140', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0140', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0141', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0141', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0142', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0142', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0143', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0143', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0144', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0144', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0145', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0145', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0146', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0146', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0147', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0147', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0148', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0148', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0149', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0149', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0150', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0150', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0151', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0151', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0152', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0152', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0153', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0153', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0154', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0154', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0155', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0155', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0156', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0156', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0157', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0157', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0158', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0158', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0159', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0159', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0160', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0160', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0161', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0161', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0162', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0162', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0163', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0163', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0164', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0164', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0165', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0165', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0166', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0166', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0167', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0167', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0168', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0168', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0169', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0169', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0170', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0170', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0171', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0171', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0172', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0172', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0173', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0173', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0174', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0174', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0175', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0175', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0176', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0176', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0177', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0177', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0178', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0178', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0179', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0179', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0180', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0180', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0181', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0181', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0182', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0182', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0183', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0183', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0184', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0184', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0185', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0185', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0186', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0186', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0187', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0187', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0188', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0188', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0189', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0189', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0190', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0190', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0191', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0191', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0192', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0192', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0193', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0193', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0194', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0194', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0195', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0195', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0196', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0196', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0197', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0197', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0198', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0198', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0199', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0199', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0200', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0200', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0201', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0201', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0202', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0202', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0203', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0203', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0204', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0204', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0205', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0205', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0206', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0206', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0207', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0207', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0208', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0208', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0209', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0209', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0210', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0210', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0211', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0211', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0212', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0212', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0213', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0213', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0214', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0214', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0215', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0215', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0216', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0216', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0217', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0217', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0218', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0218', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0219', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0219', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0220', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0220', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0221', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0221', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0222', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0222', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0223', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0223', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0224', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0224', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0225', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0225', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0226', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0226', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0227', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0227', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0228', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0228', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0229', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0229', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0230', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0230', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0231', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0231', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0232', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0232', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0233', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0233', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0234', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0234', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0235', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0235', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0236', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0236', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0237', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0237', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0238', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0238', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0239', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0239', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0240', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0240', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0241', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0241', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0242', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0242', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0243', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0243', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0244', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0244', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0245', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0245', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0246', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0246', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0247', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0247', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0248', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0248', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0249', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0249', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0250', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0250', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0251', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0251', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0252', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0252', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0253', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0253', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0254', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0254', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0255', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0255', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0256', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0256', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0257', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0257', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0258', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0258', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0259', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0259', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0260', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0260', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0261', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0261', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0262', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0262', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0263', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0263', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0264', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0264', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0265', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0265', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0266', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0266', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0267', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0267', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0268', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0268', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0269', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0269', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0270', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0270', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0271', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0271', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0272', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0272', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0273', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0273', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0274', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0274', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0275', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0275', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0276', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0276', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0277', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0277', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0278', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0278', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0279', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0279', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0280', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0280', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0281', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0281', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0282', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0282', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0283', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0283', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0284', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0284', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0285', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0285', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0286', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0286', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0287', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0287', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0288', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0288', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0289', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0289', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0290', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0290', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0291', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0291', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0292', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0292', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0293', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0293', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0294', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0294', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0295', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0295', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0296', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0296', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0297', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0297', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0298', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0298', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0299', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0299', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0300', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0300', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0301', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0301', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0302', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0302', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0303', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0303', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0304', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0304', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0305', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0305', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0306', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0306', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0307', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0307', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0308', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0308', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0309', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0309', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0310', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0310', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0311', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0311', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0312', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0312', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0313', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0313', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0314', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0314', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0315', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0315', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0316', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0316', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0317', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0317', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0318', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0318', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0319', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0319', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0320', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0320', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0321', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0321', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0322', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0322', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0323', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0323', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0324', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0324', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0325', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0325', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0326', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0326', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0327', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0327', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0328', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0328', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0329', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0329', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0330', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0330', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0331', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0331', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0332', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0332', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0333', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0333', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0334', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0334', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0335', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0335', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0336', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0336', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0337', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0337', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0338', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0338', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0339', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0339', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0340', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0340', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0341', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0341', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0342', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0342', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0343', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0343', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0344', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0344', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0345', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0345', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0346', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0346', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0347', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0347', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0348', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0348', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0349', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0349', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0350', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0350', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0351', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0351', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0352', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0352', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0353', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0353', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0354', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0354', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0355', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0355', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0356', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0356', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0357', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0357', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0358', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0358', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0359', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0359', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0360', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0360', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0361', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0361', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0362', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0362', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0363', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0363', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0364', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0364', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0365', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0365', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0366', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0366', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0367', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0367', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0368', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0368', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0369', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0369', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0370', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0370', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0371', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0371', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0372', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0372', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0373', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0373', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0374', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0374', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0375', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0375', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0376', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0376', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0377', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0377', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0378', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0378', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0379', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0379', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0380', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0380', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0381', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0381', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0382', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0382', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0383', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0383', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0384', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0384', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0385', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0385', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0386', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0386', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0387', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0387', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0388', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0388', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0389', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0389', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0390', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0390', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0391', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0391', + manifestID: '5a25b0703b0eb7478b415bd4' + }, + { + label: 'Hollar_a_3000_0392', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0392', + manifestID: '5a25b0703b0eb7478b415bd4' + } + ], + name: 'The fables of Aesop / paraphras\'d in verse, and...' + }, + '5a25b0763b0eb7478b415bd5': { + id: '5a25b0763b0eb7478b415bd5', + url: 'https://iiif.library.utoronto.ca/presentation/v2/hollar:Hollar_a_3001/manifest', + images: [ + { + label: 'Hollar_a_3001_0001', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0001', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0002', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0002', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0003', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0003', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0004', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0004', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0005', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0005', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0006', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0006', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0007', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0007', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0008', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0008', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0009', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0009', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0010', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0010', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0011', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0011', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0012', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0012', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0013', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0013', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0014', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0014', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0015', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0015', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0016', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0016', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0017', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0017', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0018', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0018', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0019', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0019', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0020', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0020', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0021', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0021', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0022', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0022', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0023', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0023', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0024', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0024', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0025', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0025', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0026', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0026', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0027', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0027', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0028', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0028', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0029', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0029', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0030', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0030', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0031', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0031', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0032', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0032', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0033', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0033', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0034', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0034', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0035', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0035', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0036', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0036', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0037', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0037', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0038', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0038', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0039', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0039', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0040', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0040', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0041', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0041', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0042', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0042', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0043', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0043', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0044', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0044', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0045', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0045', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0046', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0046', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0047', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0047', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0048', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0048', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0049', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0049', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0050', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0050', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0051', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0051', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0052', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0052', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0053', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0053', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0054', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0054', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0055', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0055', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0056', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0056', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0057', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0057', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0058', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0058', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0059', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0059', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0060', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0060', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0061', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0061', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0062', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0062', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0063', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0063', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0064', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0064', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0065', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0065', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0066', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0066', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0067', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0067', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0068', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0068', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0069', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0069', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0070', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0070', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0071', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0071', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0072', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0072', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0073', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0073', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0074', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0074', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0075', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0075', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0076', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0076', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0077', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0077', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0078', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0078', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0079', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0079', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0080', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0080', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0081', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0081', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0082', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0082', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0083', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0083', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0084', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0084', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0085', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0085', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0086', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0086', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0087', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0087', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0088', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0088', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0089', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0089', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0090', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0090', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0091', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0091', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0092', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0092', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0093', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0093', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0094', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0094', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0095', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0095', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0096', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0096', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0097', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0097', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0098', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0098', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0099', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0099', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0100', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0100', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0101', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0101', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0102', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0102', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0103', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0103', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0104', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0104', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0105', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0105', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0106', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0106', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0107', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0107', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0108', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0108', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0109', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0109', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0110', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0110', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0111', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0111', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0112', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0112', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0113', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0113', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0114', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0114', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0115', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0115', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0116', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0116', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0117', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0117', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0118', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0118', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0119', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0119', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0120', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0120', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0121', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0121', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0122', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0122', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0123', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0123', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0124', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0124', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0125', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0125', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0126', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0126', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0127', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0127', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0128', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0128', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0129', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0129', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0130', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0130', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0131', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0131', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0132', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0132', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0133', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0133', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0134', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0134', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0135', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0135', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0136', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0136', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0137', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0137', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0138', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0138', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0139', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0139', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0140', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0140', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0141', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0141', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0142', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0142', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0143', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0143', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0144', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0144', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0145', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0145', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0146', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0146', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0147', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0147', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0148', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0148', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0149', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0149', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0150', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0150', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0151', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0151', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0152', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0152', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0153', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0153', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0154', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0154', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0155', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0155', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0156', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0156', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0157', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0157', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0158', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0158', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0159', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0159', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0160', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0160', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0161', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0161', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0162', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0162', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0163', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0163', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0164', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0164', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0165', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0165', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0166', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0166', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0167', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0167', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0168', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0168', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0169', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0169', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0170', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0170', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0171', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0171', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0172', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0172', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0173', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0173', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0174', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0174', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0175', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0175', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0176', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0176', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0177', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0177', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0178', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0178', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0179', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0179', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0180', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0180', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0181', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0181', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0182', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0182', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0183', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0183', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0184', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0184', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0185', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0185', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0186', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0186', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0187', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0187', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0188', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0188', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0189', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0189', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0190', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0190', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0191', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0191', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0192', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0192', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0193', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0193', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0194', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0194', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0195', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0195', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0196', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0196', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0197', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0197', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0198', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0198', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0199', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0199', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0200', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0200', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0201', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0201', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0202', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0202', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0203', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0203', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0204', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0204', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0205', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0205', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0206', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0206', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0207', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0207', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0208', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0208', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0209', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0209', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0210', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0210', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0211', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0211', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0212', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0212', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0213', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0213', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0214', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0214', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0215', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0215', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0216', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0216', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0217', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0217', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0218', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0218', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0219', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0219', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0220', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0220', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0221', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0221', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0222', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0222', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0223', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0223', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0224', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0224', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0225', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0225', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0226', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0226', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0227', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0227', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0228', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0228', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0229', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0229', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0230', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0230', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0231', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0231', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0232', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0232', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0233', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0233', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0234', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0234', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0235', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0235', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0236', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0236', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0237', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0237', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0238', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0238', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0239', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0239', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0240', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0240', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0241', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0241', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0242', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0242', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0243', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0243', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0244', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0244', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0245', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0245', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0246', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0246', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0247', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0247', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0248', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0248', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0249', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0249', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0250', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0250', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0251', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0251', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0252', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0252', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0253', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0253', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0254', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0254', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0255', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0255', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0256', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0256', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0257', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0257', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0258', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0258', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0259', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0259', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0260', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0260', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0261', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0261', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0262', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0262', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0263', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0263', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0264', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0264', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0265', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0265', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0266', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0266', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0267', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0267', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0268', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0268', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0269', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0269', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0270', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0270', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0271', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0271', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0272', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0272', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0273', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0273', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0274', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0274', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0275', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0275', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0276', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0276', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0277', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0277', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0278', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0278', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0279', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0279', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0280', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0280', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0281', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0281', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0282', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0282', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0283', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0283', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0284', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0284', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0285', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0285', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0286', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0286', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0287', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0287', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0288', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0288', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0289', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0289', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0290', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0290', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0291', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0291', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0292', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0292', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0293', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0293', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0294', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0294', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0295', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0295', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0296', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0296', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0297', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0297', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0298', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0298', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0299', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0299', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0300', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0300', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0301', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0301', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0302', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0302', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0303', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0303', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0304', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0304', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0305', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0305', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0306', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0306', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0307', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0307', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0308', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0308', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0309', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0309', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0310', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0310', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0311', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0311', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0312', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0312', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0313', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0313', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0314', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0314', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0315', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0315', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0316', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0316', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0317', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0317', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0318', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0318', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0319', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0319', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0320', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0320', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0321', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0321', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0322', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0322', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0323', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0323', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0324', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0324', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0325', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0325', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0326', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0326', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0327', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0327', + manifestID: '5a25b0763b0eb7478b415bd5' + }, + { + label: 'Hollar_a_3001_0328', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3001_0328', + manifestID: '5a25b0763b0eb7478b415bd5' + } + ], + name: 'The history of St. Paul\'s Cathedral in London :...' + } + }, + groupIDs: [ + 'Group_5a57825a4cfad13070870df4', + 'Group_5a57825a4cfad13070870df5', + 'Group_5a57825a4cfad13070870df6', + 'Group_5a57825a4cfad13070870df7' + ], + leafIDs: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dca', + 'Leaf_5a57825a4cfad13070870dcd', + 'Leaf_5a57825a4cfad13070870dd0', + 'Leaf_5a57825a4cfad13070870dd3', + 'Leaf_5a57825a4cfad13070870dd6', + 'Leaf_5a57825a4cfad13070870dd9', + 'Leaf_5a57825a4cfad13070870ddc', + 'Leaf_5a57825a4cfad13070870ddf', + 'Leaf_5a57825a4cfad13070870de2', + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb', + 'Leaf_5a57825a4cfad13070870dee', + 'Leaf_5a57825a4cfad13070870df1' + ], + rectoIDs: [ + 'Recto_5a57825a4cfad13070870dc5', + 'Recto_5a57825a4cfad13070870dc8', + 'Recto_5a57825a4cfad13070870dcb', + 'Recto_5a57825a4cfad13070870dce', + 'Recto_5a57825a4cfad13070870dd1', + 'Recto_5a57825a4cfad13070870dd4', + 'Recto_5a57825a4cfad13070870dd7', + 'Recto_5a57825a4cfad13070870dda', + 'Recto_5a57825a4cfad13070870ddd', + 'Recto_5a57825a4cfad13070870de0', + 'Recto_5a57825a4cfad13070870de3', + 'Recto_5a57825a4cfad13070870de6', + 'Recto_5a57825a4cfad13070870de9', + 'Recto_5a57825a4cfad13070870dec', + 'Recto_5a57825a4cfad13070870def', + 'Recto_5a57825a4cfad13070870df2' + ], + versoIDs: [ + 'Verso_5a57825a4cfad13070870dc6', + 'Verso_5a57825a4cfad13070870dc9', + 'Verso_5a57825a4cfad13070870dcc', + 'Verso_5a57825a4cfad13070870dcf', + 'Verso_5a57825a4cfad13070870dd2', + 'Verso_5a57825a4cfad13070870dd5', + 'Verso_5a57825a4cfad13070870dd8', + 'Verso_5a57825a4cfad13070870ddb', + 'Verso_5a57825a4cfad13070870dde', + 'Verso_5a57825a4cfad13070870de1', + 'Verso_5a57825a4cfad13070870de4', + 'Verso_5a57825a4cfad13070870de7', + 'Verso_5a57825a4cfad13070870dea', + 'Verso_5a57825a4cfad13070870ded', + 'Verso_5a57825a4cfad13070870df0', + 'Verso_5a57825a4cfad13070870df3' + ], + Groups: { + Group_5a57825a4cfad13070870df4: { + id: 'Group_5a57825a4cfad13070870df4', + type: 'Quire', + title: 'First Quire', + tacketed: [], + sewing: [ + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dcd' + ], + nestLevel: 1, + parentID: null, + notes: [ + '5a57825a4cfad13070870df8' + ], + memberIDs: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870dc7', + 'Leaf_5a57825a4cfad13070870dca', + 'Leaf_5a57825a4cfad13070870dcd', + 'Leaf_5a57825a4cfad13070870dd0', + 'Leaf_5a57825a4cfad13070870dd3' + ], + memberType: 'Group' + }, + Group_5a57825a4cfad13070870df5: { + id: 'Group_5a57825a4cfad13070870df5', + type: 'Quire', + title: '2nd Quire', + tacketed: [ + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870dee' + ], + sewing: [], + nestLevel: 1, + parentID: null, + notes: [ + '5a57825a4cfad13070870df8' + ], + memberIDs: [ + 'Group_5a57825a4cfad13070870df6', + 'Leaf_5a57825a4cfad13070870de2', + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb', + 'Leaf_5a57825a4cfad13070870dee', + 'Leaf_5a57825a4cfad13070870df1' + ], + memberType: 'Group' + }, + Group_5a57825a4cfad13070870df6: { + id: 'Group_5a57825a4cfad13070870df6', + type: 'Quire', + title: '1st Sub Quire of 2', + tacketed: [], + sewing: [], + nestLevel: 2, + parentID: 'Group_5a57825a4cfad13070870df5', + notes: [], + memberIDs: [ + 'Group_5a57825a4cfad13070870df7', + 'Leaf_5a57825a4cfad13070870ddc', + 'Leaf_5a57825a4cfad13070870ddf' + ], + memberType: 'Group' + }, + Group_5a57825a4cfad13070870df7: { + id: 'Group_5a57825a4cfad13070870df7', + type: 'Quire', + title: '1st Sub Quire of Sub Quire 2.1', + tacketed: [], + sewing: [], + nestLevel: 3, + parentID: 'Group_5a57825a4cfad13070870df6', + notes: [], + memberIDs: [ + 'Leaf_5a57825a4cfad13070870dd6', + 'Leaf_5a57825a4cfad13070870dd9' + ], + memberType: 'Group' + } + }, + Leafs: { + Leaf_5a57825a4cfad13070870dc4: { + id: 'Leaf_5a57825a4cfad13070870dc4', + material: 'None', + type: 'Endleaf', + conjoined_to: 'Leaf_5a57825a4cfad13070870dd3', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dc5', + versoID: 'Verso_5a57825a4cfad13070870dc6', + notes: [ + '5a57825a4cfad13070870df9' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dc7: { + id: 'Leaf_5a57825a4cfad13070870dc7', + material: 'None', + type: 'Missing', + conjoined_to: 'Leaf_5a57825a4cfad13070870dd0', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dc8', + versoID: 'Verso_5a57825a4cfad13070870dc9', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dca: { + id: 'Leaf_5a57825a4cfad13070870dca', + material: 'Parchment', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dcd', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dcb', + versoID: 'Verso_5a57825a4cfad13070870dcc', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dcd: { + id: 'Leaf_5a57825a4cfad13070870dcd', + material: 'Parchment', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dca', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dce', + versoID: 'Verso_5a57825a4cfad13070870dcf', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd0: { + id: 'Leaf_5a57825a4cfad13070870dd0', + material: 'Parchment', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dc7', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dd1', + versoID: 'Verso_5a57825a4cfad13070870dd2', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd3: { + id: 'Leaf_5a57825a4cfad13070870dd3', + material: 'None', + type: 'Endleaf', + conjoined_to: 'Leaf_5a57825a4cfad13070870dc4', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df4', + rectoID: 'Recto_5a57825a4cfad13070870dd4', + versoID: 'Verso_5a57825a4cfad13070870dd5', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd6: { + id: 'Leaf_5a57825a4cfad13070870dd6', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'None', + attached_below: 'Glued (Partial)', + stub: 'None', + nestLevel: 3, + parentID: 'Group_5a57825a4cfad13070870df7', + rectoID: 'Recto_5a57825a4cfad13070870dd7', + versoID: 'Verso_5a57825a4cfad13070870dd8', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dd9: { + id: 'Leaf_5a57825a4cfad13070870dd9', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'Glued (Partial)', + attached_below: 'None', + stub: 'None', + nestLevel: 3, + parentID: 'Group_5a57825a4cfad13070870df7', + rectoID: 'Recto_5a57825a4cfad13070870dda', + versoID: 'Verso_5a57825a4cfad13070870ddb', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870ddc: { + id: 'Leaf_5a57825a4cfad13070870ddc', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870ddf', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 2, + parentID: 'Group_5a57825a4cfad13070870df6', + rectoID: 'Recto_5a57825a4cfad13070870ddd', + versoID: 'Verso_5a57825a4cfad13070870dde', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870ddf: { + id: 'Leaf_5a57825a4cfad13070870ddf', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870ddc', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 2, + parentID: 'Group_5a57825a4cfad13070870df6', + rectoID: 'Recto_5a57825a4cfad13070870de0', + versoID: 'Verso_5a57825a4cfad13070870de1', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870de2: { + id: 'Leaf_5a57825a4cfad13070870de2', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870df1', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870de3', + versoID: 'Verso_5a57825a4cfad13070870de4', + notes: [ + '5a57825a4cfad13070870df9' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870de5: { + id: 'Leaf_5a57825a4cfad13070870de5', + material: 'Other', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870dee', + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870de6', + versoID: 'Verso_5a57825a4cfad13070870de7', + notes: [ + '5a57825a4cfad13070870df8' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870de8: { + id: 'Leaf_5a57825a4cfad13070870de8', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'None', + attached_below: 'None', + stub: 'Original', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870de9', + versoID: 'Verso_5a57825a4cfad13070870dea', + notes: [ + '5a57825a4cfad13070870df8' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870deb: { + id: 'Leaf_5a57825a4cfad13070870deb', + material: 'None', + type: 'None', + conjoined_to: null, + attached_above: 'None', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870dec', + versoID: 'Verso_5a57825a4cfad13070870ded', + notes: [ + '5a57825a4cfad13070870df8' + ], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870dee: { + id: 'Leaf_5a57825a4cfad13070870dee', + material: 'None', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870de5', + attached_above: 'None', + attached_below: 'Glued (Complete)', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870def', + versoID: 'Verso_5a57825a4cfad13070870df0', + notes: [], + memberType: 'Leaf' + }, + Leaf_5a57825a4cfad13070870df1: { + id: 'Leaf_5a57825a4cfad13070870df1', + material: 'None', + type: 'None', + conjoined_to: 'Leaf_5a57825a4cfad13070870de2', + attached_above: 'Glued (Complete)', + attached_below: 'None', + stub: 'None', + nestLevel: 1, + parentID: 'Group_5a57825a4cfad13070870df5', + rectoID: 'Recto_5a57825a4cfad13070870df2', + versoID: 'Verso_5a57825a4cfad13070870df3', + notes: [], + memberType: 'Leaf' + } + }, + Rectos: { + Recto_5a57825a4cfad13070870dc5: { + id: 'Recto_5a57825a4cfad13070870dc5', + parentID: 'Leaf_5a57825a4cfad13070870dc4', + folio_number: '1R', + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0003', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0003' + }, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dc8: { + id: 'Recto_5a57825a4cfad13070870dc8', + parentID: 'Leaf_5a57825a4cfad13070870dc7', + folio_number: '2R', + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0002', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0002' + }, + script_direction: 'None', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dcb: { + id: 'Recto_5a57825a4cfad13070870dcb', + parentID: 'Leaf_5a57825a4cfad13070870dca', + folio_number: '3R', + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0001', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0001' + }, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dce: { + id: 'Recto_5a57825a4cfad13070870dce', + parentID: 'Leaf_5a57825a4cfad13070870dcd', + folio_number: '4R', + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0007', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0007' + }, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dd1: { + id: 'Recto_5a57825a4cfad13070870dd1', + parentID: 'Leaf_5a57825a4cfad13070870dd0', + folio_number: '5R', + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0009', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0009' + }, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dd4: { + id: 'Recto_5a57825a4cfad13070870dd4', + parentID: 'Leaf_5a57825a4cfad13070870dd3', + folio_number: '6R', + generated_folio_number: null, + texture: 'Hair', + image: { + manifestID: '5a25b0703b0eb7478b415bd4', + label: 'Hollar_a_3000_0011', + url: 'https://iiif.library.utoronto.ca/image/v2/hollar:Hollar_a_3000_0011' + }, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dd7: { + id: 'Recto_5a57825a4cfad13070870dd7', + parentID: 'Leaf_5a57825a4cfad13070870dd6', + folio_number: '7R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dda: { + id: 'Recto_5a57825a4cfad13070870dda', + parentID: 'Leaf_5a57825a4cfad13070870dd9', + folio_number: '8R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870ddd: { + id: 'Recto_5a57825a4cfad13070870ddd', + parentID: 'Leaf_5a57825a4cfad13070870ddc', + folio_number: '9R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'Left-to-Right', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de0: { + id: 'Recto_5a57825a4cfad13070870de0', + parentID: 'Leaf_5a57825a4cfad13070870ddf', + folio_number: '10R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de3: { + id: 'Recto_5a57825a4cfad13070870de3', + parentID: 'Leaf_5a57825a4cfad13070870de2', + folio_number: '11R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de6: { + id: 'Recto_5a57825a4cfad13070870de6', + parentID: 'Leaf_5a57825a4cfad13070870de5', + folio_number: '12R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870de9: { + id: 'Recto_5a57825a4cfad13070870de9', + parentID: 'Leaf_5a57825a4cfad13070870de8', + folio_number: '13R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870dec: { + id: 'Recto_5a57825a4cfad13070870dec', + parentID: 'Leaf_5a57825a4cfad13070870deb', + folio_number: '14R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870def: { + id: 'Recto_5a57825a4cfad13070870def', + parentID: 'Leaf_5a57825a4cfad13070870dee', + folio_number: '15R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + }, + Recto_5a57825a4cfad13070870df2: { + id: 'Recto_5a57825a4cfad13070870df2', + parentID: 'Leaf_5a57825a4cfad13070870df1', + folio_number: '16R', + generated_folio_number: null, + texture: 'Hair', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Recto' + } + }, + Versos: { + Verso_5a57825a4cfad13070870dc6: { + id: 'Verso_5a57825a4cfad13070870dc6', + parentID: 'Leaf_5a57825a4cfad13070870dc4', + folio_number: '1V', + generated_folio_number: null, + texture: 'Flesh', + image: { + manifestID: 'DIYImages', + label: 'cguk1l0u4aeewdf_copy(1).jpeg', + url: 'http://localhost:3001/images/5a5782714cfad13070870dfc_cguk1l0u4aeewdf_copy(1).jpeg' + }, + script_direction: 'None', + notes: [ + '5a57825a4cfad13070870df9', + '5a57825a4cfad13070870dfa' + ], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dc9: { + id: 'Verso_5a57825a4cfad13070870dc9', + parentID: 'Leaf_5a57825a4cfad13070870dc7', + folio_number: '2V', + generated_folio_number: null, + texture: 'Flesh', + image: { + manifestID: 'DIYImages', + label: '3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny.jpeg', + url: 'http://localhost:3001/images/5a5782714cfad13070870dfd_3c17f2b4127b1a5a8bcfc76ba9de9c9f_chiba_inu_dogs_shiba_inu_funny.jpeg' + }, + script_direction: 'None', + notes: [ + '5a57825a4cfad13070870dfa' + ], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dcc: { + id: 'Verso_5a57825a4cfad13070870dcc', + parentID: 'Leaf_5a57825a4cfad13070870dca', + folio_number: '3V', + generated_folio_number: null, + texture: 'Flesh', + image: { + manifestID: 'DIYImages', + label: '10_profile_copy(1).jpeg', + url: 'http://localhost:3001/images/5a5782714cfad13070870dff_10_profile_copy(1).jpeg' + }, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dcf: { + id: 'Verso_5a57825a4cfad13070870dcf', + parentID: 'Leaf_5a57825a4cfad13070870dcd', + folio_number: '4V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dd2: { + id: 'Verso_5a57825a4cfad13070870dd2', + parentID: 'Leaf_5a57825a4cfad13070870dd0', + folio_number: '5V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dd5: { + id: 'Verso_5a57825a4cfad13070870dd5', + parentID: 'Leaf_5a57825a4cfad13070870dd3', + folio_number: '6V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dd8: { + id: 'Verso_5a57825a4cfad13070870dd8', + parentID: 'Leaf_5a57825a4cfad13070870dd6', + folio_number: '7V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870ddb: { + id: 'Verso_5a57825a4cfad13070870ddb', + parentID: 'Leaf_5a57825a4cfad13070870dd9', + folio_number: '8V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dde: { + id: 'Verso_5a57825a4cfad13070870dde', + parentID: 'Leaf_5a57825a4cfad13070870ddc', + folio_number: '9V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870de1: { + id: 'Verso_5a57825a4cfad13070870de1', + parentID: 'Leaf_5a57825a4cfad13070870ddf', + folio_number: '10V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870de4: { + id: 'Verso_5a57825a4cfad13070870de4', + parentID: 'Leaf_5a57825a4cfad13070870de2', + folio_number: '11V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870de7: { + id: 'Verso_5a57825a4cfad13070870de7', + parentID: 'Leaf_5a57825a4cfad13070870de5', + folio_number: '12V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870dea: { + id: 'Verso_5a57825a4cfad13070870dea', + parentID: 'Leaf_5a57825a4cfad13070870de8', + folio_number: '13V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'None', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870ded: { + id: 'Verso_5a57825a4cfad13070870ded', + parentID: 'Leaf_5a57825a4cfad13070870deb', + folio_number: '14V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'Right-To-Left', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870df0: { + id: 'Verso_5a57825a4cfad13070870df0', + parentID: 'Leaf_5a57825a4cfad13070870dee', + folio_number: '15V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'Right-To-Left', + notes: [], + memberType: 'Verso' + }, + Verso_5a57825a4cfad13070870df3: { + id: 'Verso_5a57825a4cfad13070870df3', + parentID: 'Leaf_5a57825a4cfad13070870df1', + folio_number: '16V', + generated_folio_number: null, + texture: 'Flesh', + image: {}, + script_direction: 'Right-To-Left', + notes: [], + memberType: 'Verso' + } + }, + Notes: { + '5a57825a4cfad13070870df8': { + id: '5a57825a4cfad13070870df8', + title: 'Black ink', + type: 'Ink', + description: 'Some black ink over here\n', + show: true, + objects: { + Group: [ + 'Group_5a57825a4cfad13070870df4', + 'Group_5a57825a4cfad13070870df5' + ], + Leaf: [ + 'Leaf_5a57825a4cfad13070870de5', + 'Leaf_5a57825a4cfad13070870de8', + 'Leaf_5a57825a4cfad13070870deb' + ], + Recto: [], + Verso: [] + } + }, + '5a57825a4cfad13070870df9': { + id: '5a57825a4cfad13070870df9', + title: 'John\'s hand', + type: 'Hand', + description: 'Look ! ', + show: false, + objects: { + Group: [], + Leaf: [ + 'Leaf_5a57825a4cfad13070870dc4', + 'Leaf_5a57825a4cfad13070870de2' + ], + Recto: [], + Verso: [ + 'Verso_5a57825a4cfad13070870dc6' + ] + } + }, + '5a57825a4cfad13070870dfa': { + id: '5a57825a4cfad13070870dfa', + title: 'Fire', + type: 'Damage', + description: 'Some burnt marks', + show: true, + objects: { + Group: [], + Leaf: [ + 'Leaf_5a57825a4cfad13070870dca', + 'Leaf_5a57825a4cfad13070870dcd', + 'Leaf_5a57825a4cfad13070870dd0' + ], + Recto: [ + 'Recto_5a57825a4cfad13070870dc8' + ], + Verso: [ + 'Verso_5a57825a4cfad13070870dc6', + 'Verso_5a57825a4cfad13070870dc9' + ] + } + } + } + }, + managerMode: 'collationManager', + collationManager: { + selectedObjects: { + type: 'Leaf', + members: [ + 'Leaf_5a57825a4cfad13070870dc4' + ], + lastSelected: 'Leaf_5a57825a4cfad13070870dc4' + }, + viewMode: 'VISUAL', + visibleAttributes: { + group: { + type: false, + title: false + }, + leaf: { + type: false, + material: false, + conjoined_leaf_order: false, + attached_below: false, + attached_above: false, + stub: false + }, + side: { + folio_number: false, + texture: false, + script_direction: false, + uri: false + } + }, + defaultAttributes: { + leaf: [ + { + name: 'type', + displayName: 'Type', + options: [ + 'None', + 'Original', + 'Added', + 'Missing', + 'Hook', + 'Endleaf', + 'Replaced' + ], + isDropdown: true + }, + { + name: 'material', + displayName: 'Material', + options: [ + 'None', + 'Parchment', + 'Paper', + 'Other' + ], + isDropdown: true + }, + { + name: 'conjoined_to', + displayName: 'Conjoined To', + isDropdown: true + }, + { + name: 'attached_above', + displayName: 'Attached Above', + options: [ + 'None', + 'Glued (Partial)', + 'Glued (Complete)', + 'Glued (Drumming)', + 'Other' + ], + isDropdown: true + }, + { + name: 'attached_below', + displayName: 'Attached Below', + options: [ + 'None', + 'Glued (Partial)', + 'Glued (Complete)', + 'Glued (Drumming)', + 'Other' + ], + isDropdown: true + }, + { + name: 'stub', + displayName: 'Stub', + options: [ + 'None', + 'Original', + 'Added' + ], + isDropdown: true + } + ], + group: [ + { + name: 'type', + displayName: 'Type', + options: [ + 'Quire', + 'Booklet' + ], + isDropdown: true + }, + { + name: 'title', + displayName: 'Title' + } + ], + side: [ + { + name: 'texture', + displayName: 'Texture', + options: [ + 'None', + 'Hair', + 'Flesh', + 'Felt', + 'Wire' + ], + isDropdown: true + }, + { + name: 'folio_number', + displayName: 'Folio Number' + }, + { + name: 'script_direction', + displayName: 'Script Direction', + options: [ + 'None', + 'Left-to-Right', + 'Right-To-Left', + 'Top-To-Bottom' + ], + isDropdown: true + }, + { + name: 'uri', + displayName: 'URI' + } + ], + note: [ + { + name: 'title', + displayName: 'Title' + }, + { + name: 'type', + displayName: 'Type', + isDropdown: true + }, + { + name: 'description', + displayName: 'Description' + } + ] + }, + filters: { + filterPanelOpen: false, + Groups: [], + Leafs: [], + Sides: [], + Notes: [], + GroupsOfMatchingLeafs: [], + LeafsOfMatchingSides: [], + GroupsOfMatchingSides: [], + GroupsOfMatchingNotes: [], + LeafsOfMatchingNotes: [], + SidesOfMatchingNotes: [], + active: false, + hideOthers: false, + queries: [ + { + type: null, + attribute: '', + attributeIndex: '', + values: [], + condition: '', + conjunction: '' + } + ], + selection: '' + }, + flashItems: { + leaves: [], + groups: [] + }, + visualizations: { + tacketed: '', + sewing: '' + } + }, + notesManager: { + activeTab: 'MANAGE' + }, + imageManager: { + activeTab: 'MANAGE', + manageSources: { + error: '' + } + }, +} \ No newline at end of file diff --git a/viscoll-app/assetsTransformer.js b/viscoll-app/assetsTransformer.js new file mode 100644 index 00000000..5eadded9 --- /dev/null +++ b/viscoll-app/assetsTransformer.js @@ -0,0 +1,7 @@ +const path = require('path'); + +module.exports = { + process(src, filename, config, options) { + return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; + }, +}; diff --git a/viscoll-app/package-lock.json b/viscoll-app/package-lock.json new file mode 100644 index 00000000..8c7b49b8 --- /dev/null +++ b/viscoll-app/package-lock.json @@ -0,0 +1,19531 @@ +{ + "name": "viscoll-app", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/compat-data": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.6.tgz", + "integrity": "sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, + "@babel/core": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", + "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.4", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.8.3.tgz", + "integrity": "sha512-JT8mfnpTkKNCboTqZsQTdGo3l3Ik3l7QIt9hh0O9DYiwVel37VoJpILKM4YFbP2euF32nkQSb+F9cUk9b7DDXQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "esutils": "^2.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz", + "integrity": "sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", + "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.8.6", + "browserslist": "^4.9.1", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.6.tgz", + "integrity": "sha512-klTBDdsr+VFFqaDHm5rR69OpEQtO2Qv8ECxHS1mNhJJvaHArR6a1xTf5K/eZW7eZpJbhCx3NW1Yt/sKsLXLblg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz", + "integrity": "sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.6.0" + }, + "dependencies": { + "regexpu-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", + "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.1.0", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "dev": true + }, + "regjsparser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.3.tgz", + "integrity": "sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "@babel/helper-define-map": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-transforms": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz", + "integrity": "sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.8.6", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-replace-supers": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-wrap-function": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", + "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", + "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz", + "integrity": "sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", + "integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz", + "integrity": "sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz", + "integrity": "sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz", + "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", + "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", + "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz", + "integrity": "sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz", + "integrity": "sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-define-map": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz", + "integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.8.3.tgz", + "integrity": "sha512-g/6WTWG/xbdd2exBBzMfygjX/zw4eyNC4X8pRaq7aRHRoDUCzAIu3kGYIXviOv8BjCuWm8vDBwjHcjiRNgXrPA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-flow": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz", + "integrity": "sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz", + "integrity": "sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz", + "integrity": "sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz", + "integrity": "sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz", + "integrity": "sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.7.tgz", + "integrity": "sha512-brYWaEPTRimOctz2NDA3jnBbDi7SVN2T4wYuu0aqSzxC3nozFZngGaw29CJ9ZPweB7k+iFmZuoG3IVPIcXmD2g==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.8.7", + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.8.3.tgz", + "integrity": "sha512-glrzN2U+egwRfkNFtL34xIBYTxbbUF2qJTP8HD3qETBBqzAWSeNB821X0GjU06+dNpq/UyCIjI72FmGE5NNkQQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz", + "integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.8.3.tgz", + "integrity": "sha512-r0h+mUiyL595ikykci+fbwm9YzmuOrUBi0b+FDIKmi3fPQyFokWVEMJnRWHJPPQEjyFJyna9WZC6Viv6UHSv1g==", + "dev": true, + "requires": { + "@babel/helper-builder-react-jsx": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.8.3.tgz", + "integrity": "sha512-01OT7s5oa0XTLf2I8XGsL8+KqV9lx3EZV+jxn/L2LQ97CGKila2YMroTkCEIE0HV/FF7CMSRsIAybopdN9NTdg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.8.3.tgz", + "integrity": "sha512-PLMgdMGuVDtRS/SzjNEQYUT8f4z1xb2BAT54vM1X5efkVuYBf5WyGUMbpmARcfq3NaglIwz08UVQK4HHHbC6ag==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.2.tgz", + "integrity": "sha512-V4+lGplCM/ikqi5/mkkpJ06e9Bujq1NFmNLvsCs56zg3ZbzrnUzAtizZ24TXxtRX/W2jcdScwQCnbL0CICTFkQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" + } + } + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", + "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.8.3.tgz", + "integrity": "sha512-/vqUt5Yh+cgPZXXjmaG9NT8aVfThKk7G4OqkVhrXqwsC5soMn/qTCxs36rZ2QFhpfTJcjw4SNDIZ4RUb8OL4jQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", + "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.7.tgz", + "integrity": "sha512-7O0UsPQVNKqpHeHLpfvOG4uXmlw+MOxYvUv6Otc9uH5SYMIxvF6eBdjkWvC3f9G+VXe0RsNExyAQBeTRug/wqQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-typescript": "^7.8.3" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/preset-env": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.7.tgz", + "integrity": "sha512-BYftCVOdAYJk5ASsznKAUl53EMhfBbr8CJ1X+AJLfGPscQkwJFiaV/Wn9DPH/7fzm2v6iRYJKYHSqyynTGw0nw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.8.6", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.8.3", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.8.3", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.8.6", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.8.3", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.8.6", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/plugin-transform-modules-commonjs": "^7.8.3", + "@babel/plugin-transform-modules-systemjs": "^7.8.3", + "@babel/plugin-transform-modules-umd": "^7.8.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.8.7", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/types": "^7.8.7", + "browserslist": "^4.8.5", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-react": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.8.3.tgz", + "integrity": "sha512-9hx0CwZg92jGb7iHYQVgi0tOEHP/kM60CtWJQnmbATSPIQQ2xYzfoCI3EdqAhFBeeJwYMdWQuDUHMsuDbH9hyQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-transform-react-display-name": "^7.8.3", + "@babel/plugin-transform-react-jsx": "^7.8.3", + "@babel/plugin-transform-react-jsx-self": "^7.8.3", + "@babel/plugin-transform-react-jsx-source": "^7.8.3" + } + }, + "@babel/preset-typescript": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.8.3.tgz", + "integrity": "sha512-qee5LgPGui9zQ0jR1TeU5/fP9L+ovoArklEqY12ek8P/wV5ZeM/VYSQYwICeoT6FfpJTekG9Ilay5PhwsOpMHA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-transform-typescript": "^7.8.3" + } + }, + "@babel/runtime": { + "version": "7.0.0-beta.42", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.42.tgz", + "integrity": "sha512-iOGRzUoONLOtmCvjUsZv3mZzgCT6ljHQY5fr1qG1QIiJQwtM7zbPWGGpa3QWETq+UqwWyJnoi5XZDZRwZDFciQ==", + "requires": { + "core-js": "^2.5.3", + "regenerator-runtime": "^0.11.1" + } + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", + "dev": true + }, + "@csstools/normalize.css": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", + "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==", + "dev": true + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "dev": true + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "dev": true + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "dev": true + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "dev": true, + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig==", + "dev": true + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ==", + "dev": true + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz", + "integrity": "sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w==", + "dev": true + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz", + "integrity": "sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w==", + "dev": true + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz", + "integrity": "sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w==", + "dev": true + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz", + "integrity": "sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w==", + "dev": true + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz", + "integrity": "sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw==", + "dev": true + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz", + "integrity": "sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw==", + "dev": true + }, + "@svgr/babel-preset": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.3.tgz", + "integrity": "sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A==", + "dev": true, + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^4.2.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^4.2.0", + "@svgr/babel-plugin-svg-dynamic-title": "^4.3.3", + "@svgr/babel-plugin-svg-em-dimensions": "^4.2.0", + "@svgr/babel-plugin-transform-react-native-svg": "^4.2.0", + "@svgr/babel-plugin-transform-svg-component": "^4.2.0" + } + }, + "@svgr/core": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.3.tgz", + "integrity": "sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w==", + "dev": true, + "requires": { + "@svgr/plugin-jsx": "^4.3.3", + "camelcase": "^5.3.1", + "cosmiconfig": "^5.2.1" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz", + "integrity": "sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@svgr/plugin-jsx": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz", + "integrity": "sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w==", + "dev": true, + "requires": { + "@babel/core": "^7.4.5", + "@svgr/babel-preset": "^4.3.3", + "@svgr/hast-util-to-babel-ast": "^4.3.2", + "svg-parser": "^2.0.0" + } + }, + "@svgr/plugin-svgo": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz", + "integrity": "sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w==", + "dev": true, + "requires": { + "cosmiconfig": "^5.2.1", + "merge-deep": "^3.0.2", + "svgo": "^1.2.2" + } + }, + "@svgr/webpack": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-4.3.3.tgz", + "integrity": "sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg==", + "dev": true, + "requires": { + "@babel/core": "^7.4.5", + "@babel/plugin-transform-react-constant-elements": "^7.0.0", + "@babel/preset-env": "^7.4.5", + "@babel/preset-react": "^7.0.0", + "@svgr/core": "^4.3.3", + "@svgr/plugin-jsx": "^4.3.3", + "@svgr/plugin-svgo": "^4.3.1", + "loader-utils": "^1.2.3" + } + }, + "@types/babel__core": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.6.tgz", + "integrity": "sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.9.tgz", + "integrity": "sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "13.7.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz", + "integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/q": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", + "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", + "dev": true + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/yargs": { + "version": "13.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", + "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.22.0.tgz", + "integrity": "sha512-BvxRLaTDVQ3N+Qq8BivLiE9akQLAOUfxNHIEhedOcg8B2+jY8Rc4/D+iVprvuMX1AdezFYautuGDwr9QxqSxBQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.22.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.22.0.tgz", + "integrity": "sha512-sJt1GYBe6yC0dWOQzXlp+tiuGglNhJC9eXZeC8GBVH98Zv9jtatccuhz0OF5kC/DwChqsNfghHx7OlIDQjNYAQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.22.0", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.22.0.tgz", + "integrity": "sha512-FaZKC1X+nvD7qMPqKFUYHz3H0TAioSVFGvG29f796Nc5tBluoqfHgLbSFKsh7mKjRoeTm8J9WX2Wo9EyZWjG7w==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.22.0", + "@typescript-eslint/typescript-estree": "2.22.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz", + "integrity": "sha512-2HFZW2FQc4MhIBB8WhDm9lVFaBDy6h9jGrJ4V2Uzxe/ON29HCHBTj3GkgcsgMWfsl2U5as+pTOr30Nibaw7qRQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true + }, + "adjust-sourcemap-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz", + "integrity": "sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==", + "dev": true, + "requires": { + "assert": "1.4.1", + "camelcase": "5.0.0", + "loader-utils": "1.2.3", + "object-path": "0.11.4", + "regex-parser": "2.2.10" + }, + "dependencies": { + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + } + } + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "airbnb-prop-types": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz", + "integrity": "sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ==", + "requires": { + "array.prototype.find": "^2.0.4", + "function.prototype.name": "^1.1.0", + "has": "^1.0.3", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0", + "prop-types": "^15.7.2", + "prop-types-exact": "^1.2.0", + "react-is": "^16.8.6" + } + }, + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + } + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.find": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", + "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + } + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz", + "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==", + "dev": true, + "requires": { + "browserslist": "^4.8.3", + "caniuse-lite": "^1.0.30001020", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.26", + "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "dev": true + }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "axobject-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz", + "integrity": "sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + } + } + }, + "babel-eslint": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", + "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "babel-extract-comments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", + "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", + "dev": true, + "requires": { + "babylon": "^6.18.0" + } + }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "esutils": "^2.0.2" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", + "dev": true, + "requires": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "babel-loader": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", + "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "babel-plugin-named-asset-import": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz", + "integrity": "sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA==", + "dev": true + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", + "dev": true + }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", + "dev": true, + "requires": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-react-display-name": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", + "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", + "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "dev": true, + "requires": { + "babel-helper-builder-react-jsx": "^6.24.1", + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-self": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", + "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-source": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", + "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "dev": true + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + } + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-preset-flow": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", + "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", + "dev": true, + "requires": { + "babel-plugin-transform-flow-strip-types": "^6.22.0" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "babel-preset-react": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", + "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.3.13", + "babel-plugin-transform-react-display-name": "^6.23.0", + "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-plugin-transform-react-jsx-self": "^6.22.0", + "babel-plugin-transform-react-jsx-source": "^6.22.0", + "babel-preset-flow": "^6.23.0" + } + }, + "babel-preset-react-app": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.1.tgz", + "integrity": "sha512-YkWP2UwY//TLltNlEBRngDOrYhvSLb+CA330G7T9M5UhGEMWe+JK/8IXJc5p2fDTSfSiETf+PY0+PYXFMix81Q==", + "dev": true, + "requires": { + "@babel/core": "7.8.4", + "@babel/plugin-proposal-class-properties": "7.8.3", + "@babel/plugin-proposal-decorators": "7.8.3", + "@babel/plugin-proposal-numeric-separator": "7.8.3", + "@babel/plugin-transform-flow-strip-types": "7.8.3", + "@babel/plugin-transform-react-display-name": "7.8.3", + "@babel/plugin-transform-runtime": "7.8.3", + "@babel/preset-env": "7.8.4", + "@babel/preset-react": "7.8.3", + "@babel/preset-typescript": "7.8.3", + "@babel/runtime": "7.8.4", + "babel-plugin-macros": "2.8.0", + "babel-plugin-transform-react-remove-prop-types": "0.4.24" + }, + "dependencies": { + "@babel/preset-env": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.4.tgz", + "integrity": "sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.8.4", + "@babel/helper-compilation-targets": "^7.8.4", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.8.3", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.8.3", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.8.3", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.8.3", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.8.4", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/plugin-transform-modules-commonjs": "^7.8.3", + "@babel/plugin-transform-modules-systemjs": "^7.8.3", + "@babel/plugin-transform-modules-umd": "^7.8.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.8.4", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.3", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/types": "^7.8.3", + "browserslist": "^4.8.5", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/runtime": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", + "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, + "babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", + "dev": true, + "requires": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "dev": true, + "requires": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "bowser": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", + "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001030", + "electron-to-chromium": "^1.3.363", + "node-releases": "^1.1.50" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camel-case": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", + "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", + "dev": true, + "requires": { + "pascal-case": "^3.1.1", + "tslib": "^1.10.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001032", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001032.tgz", + "integrity": "sha512-8joOm7BwcpEN4BfVHtfh0hBXSAPVYk+eUIcNntGtMkUWy/6AKRCDZINCLe3kB1vHhT2vBxBF85Hh9VlPXi/qjA==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chain-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz", + "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "dev": true, + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" + } + }, + "chokidar": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "clientjs": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/clientjs/-/clientjs-0.1.11.tgz", + "integrity": "sha1-Rm4bE8Ipo8u9hIT5hTHc1lj4EFQ=" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", + "dev": true, + "requires": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + } + }, + "clsx": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.0.4.tgz", + "integrity": "sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=", + "dev": true, + "requires": { + "arity-n": "^1.0.4" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "confusing-browser-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", + "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", + "dev": true + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", + "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + }, + "core-js-compat": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", + "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", + "dev": true, + "requires": { + "browserslist": "^4.8.3", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "requires": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "css-loader": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", + "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.23", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.1.1", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.2", + "schema-utils": "^2.6.0" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.2.tgz", + "integrity": "sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==", + "dev": true, + "requires": { + "css-tree": "1.0.0-alpha.37" + } + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dev": true, + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", + "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", + "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", + "dev": true, + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.368", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.368.tgz", + "integrity": "sha512-fqzDipW3p+uDkHUHFPrdW3wINRKcJsbnJwBD7hgaQEQwcuLSvNLw6SeUp5gKDpTbmTl7zri7IZfhsdTUTnygJg==", + "dev": true + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "enzyme": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-2.9.1.tgz", + "integrity": "sha1-B9XOaRJBJA+4F78sSxjW5TAkDfY=", + "dev": true, + "requires": { + "cheerio": "^0.22.0", + "function.prototype.name": "^1.0.0", + "is-subset": "^0.1.1", + "lodash": "^4.17.4", + "object-is": "^1.0.1", + "object.assign": "^4.0.4", + "object.entries": "^1.0.4", + "object.values": "^1.0.4", + "prop-types": "^15.5.10", + "uuid": "^3.0.1" + } + }, + "enzyme-adapter-react-16": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz", + "integrity": "sha512-kC8pAtU2Jk3OJ0EG8Y2813dg9Ol0TXi7UNxHzHiWs30Jo/hj7alc//G1YpKUsPP1oKl9X+Lkx+WlGJpPYA+nvw==", + "requires": { + "enzyme-adapter-utils": "^1.3.0", + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "object.values": "^1.0.4", + "prop-types": "^15.6.0", + "react-reconciler": "^0.7.0", + "react-test-renderer": "^16.0.0-0" + }, + "dependencies": { + "react-test-renderer": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz", + "integrity": "sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.13.6" + } + } + } + }, + "enzyme-adapter-utils": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.11.0.tgz", + "integrity": "sha512-0VZeoE9MNx+QjTfsjmO1Mo+lMfunucYB4wt5ficU85WB/LoetTJrbuujmHP3PJx6pSoaAuLA+Mq877x4LoxdNg==", + "requires": { + "airbnb-prop-types": "^2.12.0", + "function.prototype.name": "^1.1.0", + "object.assign": "^4.1.0", + "object.fromentries": "^2.0.0", + "prop-types": "^15.7.2", + "semver": "^5.6.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==" + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", + "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "globals": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", + "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "eslint-config-react-app": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.0.tgz", + "integrity": "sha512-WrHjoGpKr1kLLiWDD81tme9jMM0hk5cMxasLSdyno6DdPt+IfLOrDJBVo6jN7tn4y1nzhs43TmUaZWO6Sf0blw==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.9" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-loader": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-3.0.3.tgz", + "integrity": "sha512-+YRqB95PnNvxNp1HEjQmvf9KNvCin5HXYYseOXVC2U0KEcw4IkQ2IQEBG46j7+gW39bMzeu0GsUhVbBY3Votpw==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "loader-fs-cache": "^1.0.2", + "loader-utils": "^1.2.3", + "object-hash": "^2.0.1", + "schema-utils": "^2.6.1" + } + }, + "eslint-module-utils": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", + "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz", + "integrity": "sha512-W5hLjpFfZyZsXfo5anlu7HM970JBDqbEshAJUkeczP6BFCIfJXuiIBQXyberLRtOStT0OGPF8efeTbxlHk4LpQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "eslint-plugin-import": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz", + "integrity": "sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", + "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.5", + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.2", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^7.0.2", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", + "dev": true + } + } + }, + "eslint-plugin-react": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.0.tgz", + "integrity": "sha512-p+PGoGeV4SaZRDsXqdj9OWcOrOpZn8gXoGPcIQTzo2IDMbAKhNDnME9myZWqO3Ic4R3YmwAZ1lDjWl2R2hMUVQ==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.3", + "object.entries": "^1.1.1", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.14.2" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object.entries": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", + "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz", + "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==", + "dev": true + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.0.tgz", + "integrity": "sha512-Xs8airJ7RQolnDIbLtRutmfvSsAe0xqMMAantCN/GMoqf81TFbeI1T7Jpd56qYu1uuh32dOG5W/X9uO+ghPXzA==", + "dev": true, + "requires": { + "acorn": "^7.1.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", + "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + } + }, + "file-saver": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filesize": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", + "integrity": "sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "follow-redirects": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.10.0.tgz", + "integrity": "sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==", + "dev": true, + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "fork-ts-checker-webpack-plugin": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz", + "integrity": "sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "chalk": "^2.4.1", + "chokidar": "^3.3.0", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz", + "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "is-callable": "^1.1.3" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "fuse.js": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.5.tgz", + "integrity": "sha512-s9PGTaQIkT69HaeoTVjwGsLfb8V8ScJLx5XGFcKHg0MqLUH/UZ4EKOtqtXX9k7AFqCGxD1aJmYb8Q5VYDibVRQ==" + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "dependencies": { + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "harmony-reflect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz", + "integrity": "sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "history": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz", + "integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^2.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^0.4.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", + "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", + "dev": true + }, + "html-minifier-terser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.0.4.tgz", + "integrity": "sha512-fHwmKQ+GzhlqdxEtwrqLT7MSuheiA+rif5/dZgbz3GjoMXJzcRzy1L9NXoiiyxrnap+q5guSiv8Tz5lrh9g42g==", + "dev": true, + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + } + } + }, + "html-webpack-plugin": { + "version": "4.0.0-beta.11", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz", + "integrity": "sha512-4Xzepf0qWxf8CGg7/WQM5qBB2Lc/NFI7MhU59eUDTkuQp3skZczH4UA1d6oQyDEIoMDgERVhRyTdtUPZ5s5HBg==", + "dev": true, + "requires": { + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + }, + "dependencies": { + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-parser-js": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "dev": true + }, + "http-proxy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "hyphenate-style-name": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz", + "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=", + "dev": true, + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "immer": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", + "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==", + "dev": true + }, + "immutability-helper": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.9.1.tgz", + "integrity": "sha512-r/RmRG8xO06s/k+PIaif2r5rGc3j4Yhc01jSBfwPCXDLYZwp/yxralI37Df1mwmuzcCsen/E/ITKcTEvc1PQmQ==", + "requires": { + "invariant": "^2.2.0" + } + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inline-style-prefixer": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz", + "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=", + "requires": { + "bowser": "^1.7.3", + "css-in-js-utils": "^2.0.0" + } + }, + "inquirer": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.6.tgz", + "integrity": "sha512-7SVO4h+QIdMq6XcqIqrNte3gS5MzCCKZdsq9DO4PJziBFNYzP3PGFbDjgadDb//MCahzgjCxvQ/O2wa7kx9o4w==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-docker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0" + } + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-jsdom-fourteen": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-1.0.1.tgz", + "integrity": "sha512-DojMX1sY+at5Ep+O9yME34CdidZnO3/zfPh8UW+918C5fIZET5vCjfkegixmsi7AtdYfkr4bPlIzmWnlvQkP7Q==", + "dev": true, + "requires": { + "@jest/environment": "^24.3.0", + "@jest/fake-timers": "^24.3.0", + "@jest/types": "^24.3.0", + "jest-mock": "^24.0.0", + "jest-util": "^24.0.0", + "jsdom": "^14.1.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "fsevents": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "optional": true + } + } + } + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-watch-typeahead": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.4.2.tgz", + "integrity": "sha512-f7VpLebTdaXs81rg/oj4Vg/ObZy2QtGzAmGLNsqUS5G5KtSN68tFcIsbvNODfNyQxU78g7D8x77o3bgfBTR+2Q==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.1", + "jest-regex-util": "^24.9.0", + "jest-watcher": "^24.3.0", + "slash": "^3.0.0", + "string-length": "^3.1.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "string-length": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", + "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^5.2.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-file-download": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.7.tgz", + "integrity": "sha512-9AQYwIpgTz3BqKQQ9kZldCXd0BekFmxvUguEVJwVlTe9cON1slRaT+hqctEbjoXbz9Aj+8Qgl3841zYWeCstuA==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + } + } + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsx-ast-utils": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", + "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" + } + }, + "jszip": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.1.tgz", + "integrity": "sha512-iCMBbo4eE5rb1VCpm5qXOAaUiRKRUKiItn8ah2YQQx9qymmSAY98eyQfioChEYcVQLh0zxJ3wS4A0mh90AVPvw==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "keycode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", + "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=" + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "dev": true, + "requires": { + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "requires": { + "immediate": "~3.0.5" + } + }, + "linear-layout-vector": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/linear-layout-vector/-/linear-layout-vector-0.0.1.tgz", + "integrity": "sha1-OYEU1zA7bsx/1rJzr3uEAdi6nHA=" + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-fs-cache": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", + "dev": true, + "requires": { + "find-cache-dir": "^0.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "localforage": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz", + "integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==", + "requires": { + "lie": "3.1.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", + "dev": true + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.isfinite": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.2.0.tgz", + "integrity": "sha1-qmn/uTo36C+rDOGIYmVfkXTO0zk=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=", + "dev": true + }, + "lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=", + "dev": true + }, + "lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "loglevel": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", + "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "material-ui": { + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.19.4.tgz", + "integrity": "sha1-ypzcqKqLtZTfrF2zjsn/BFoyNYc=", + "requires": { + "babel-runtime": "^6.23.0", + "inline-style-prefixer": "^3.0.2", + "keycode": "^2.1.8", + "lodash.merge": "^4.6.0", + "lodash.throttle": "^4.1.1", + "prop-types": "^15.5.7", + "react-event-listener": "^0.5.1", + "react-transition-group": "^1.2.1", + "recompose": "^0.26.0", + "simple-assign": "^0.1.0", + "warning": "^3.0.0" + } + }, + "material-ui-chip-input": { + "version": "0.18.8", + "resolved": "https://registry.npmjs.org/material-ui-chip-input/-/material-ui-chip-input-0.18.8.tgz", + "integrity": "sha512-QrKHmnleXYtVOn7OTPpbe2CJrXf2eIjys2STj9FD1ARSWKJv+bjiby0xNG4qBhDNpmcM8q/XC7tT30wdrisDdQ==", + "requires": { + "prop-types": "^15.5.7" + } + }, + "material-ui-superselectfield": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/material-ui-superselectfield/-/material-ui-superselectfield-1.10.0.tgz", + "integrity": "sha512-kCh40oTRJMP1BKL1fgARNhVyfvuyTeDKN9sGjL+gsH9F1BisZedC/CxyMXYQNHsNBZBA4xcK7MYSo4WZ+ENeZA==", + "requires": { + "react-infinite": "^0.13.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "merge-deep": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz", + "integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "dev": true + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "dev": true, + "requires": { + "mime-db": "1.43.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz", + "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-forge": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-releases": { + "version": "1.1.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", + "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-hash": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", + "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==", + "dev": true + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-path": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", + "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", + "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.11.0", + "function-bind": "^1.1.1", + "has": "^1.0.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.0.2.tgz", + "integrity": "sha512-70E/pFTPr7nZ9nLDPNTcj3IVqnNvKuP4VsBmoKV9YGTnChe0mlS3C4qM7qKarhZ8rGaHKLfo+vBTHXDp6ZSyLQ==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", + "dev": true + } + } + }, + "openseadragon": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/openseadragon/-/openseadragon-2.4.0.tgz", + "integrity": "sha512-e2fsoEiCDkmwc3vyrv396lkYu4c9CGlpbSX4kYE07eW0ZlEF5DnLbeY2PhqPTskkC1jlsPu2TGZwaZ9VhaAn/w==" + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimize-css-assets-webpack-plugin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", + "integrity": "sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==", + "dev": true, + "requires": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^3.0.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "paper": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/paper/-/paper-0.11.8.tgz", + "integrity": "sha512-3O5o8SntwieIlBmHjWS4hAYESiZ0yb++Outz9Rr5AN/ZFK7jfVGLANjqyIvEKIpGMImf2fQCkW64n8dkYheP5g==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "param-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", + "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", + "dev": true, + "requires": { + "dot-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", + "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", + "dev": true, + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "pnp-webpack-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.0.tgz", + "integrity": "sha512-ZcMGn/xF/fCOq+9kWMP9vVVxjIkMCja72oy3lziR7UHy0hHFZ57iVpQ71OtveVbmzeCmphBg8pxNdk/hlK99aQ==", + "dev": true, + "requires": { + "ts-pnp": "^1.1.2" + } + }, + "portfinder": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", + "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "postcss-browser-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz", + "integrity": "sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig==", + "dev": true, + "requires": { + "postcss": "^7" + } + }, + "postcss-calc": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "dev": true, + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "dev": true, + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "dev": true, + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz", + "integrity": "sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-font-variant": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz", + "integrity": "sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-initial": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.2.tgz", + "integrity": "sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA==", + "dev": true, + "requires": { + "lodash.template": "^4.5.0", + "postcss": "^7.0.2" + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" + } + }, + "postcss-modules-scope": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz", + "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-normalize": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-8.0.1.tgz", + "integrity": "sha512-rt9JMS/m9FHIRroDDBGSMsyW1c0fkvOJPy62ggxSHUldJO7B195TqFMqIf+lY5ezpDcYOV4j86aUp3/XbxzCCQ==", + "dev": true, + "requires": { + "@csstools/normalize.css": "^10.1.0", + "browserslist": "^4.6.2", + "postcss": "^7.0.17", + "postcss-browser-comments": "^3.0.0", + "sanitize.css": "^10.0.0" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "dev": true, + "requires": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-safe-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", + "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-not": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz", + "integrity": "sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", + "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", + "dev": true + }, + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "pretty-bytes": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", + "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz", + "integrity": "sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.4" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "prop-types-exact": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz", + "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==", + "requires": { + "has": "^1.0.3", + "object.assign": "^4.1.0", + "reflect.ownkeys": "^0.2.0" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dev": true, + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + } + } + }, + "react": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", + "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", + "requires": { + "create-react-class": "^15.6.0", + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-addons-test-utils": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-addons-test-utils/-/react-addons-test-utils-15.6.2.tgz", + "integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=", + "dev": true + }, + "react-app-polyfill": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz", + "integrity": "sha512-OfBnObtnGgLGfweORmdZbyEz+3dgVePQBb3zipiaDsMHV1NpWm0rDFYIVXFV/AK+x4VIIfWHhrdMIeoTLyRr2g==", + "dev": true, + "requires": { + "core-js": "^3.5.0", + "object-assign": "^4.1.1", + "promise": "^8.0.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.3", + "whatwg-fetch": "^3.0.0" + }, + "dependencies": { + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", + "dev": true + }, + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "dev": true, + "requires": { + "asap": "~2.0.6" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, + "react-detect-offline": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-detect-offline/-/react-detect-offline-1.0.6.tgz", + "integrity": "sha512-qcP5SINR1cFXdJwPfx3ia6cOuuImQYC3GnLEIp+4ARe3O+GHwp2i1yUlSDbFVe7Sovjdr8djtDMynIIaYXyBFw==" + }, + "react-dev-utils": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz", + "integrity": "sha512-MwrvQW2TFjLblhqpDNeqCXHBkz3G5vc7k4wntgutAJZX4ia3o07eGKo6uYGhUOeJ0hfOxcpJFNFk7+4XCc1S8g==", + "dev": true, + "requires": { + "@babel/code-frame": "7.8.3", + "address": "1.1.2", + "browserslist": "4.8.6", + "chalk": "2.4.2", + "cross-spawn": "7.0.1", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "2.0.0", + "filesize": "6.0.1", + "find-up": "4.1.0", + "fork-ts-checker-webpack-plugin": "3.1.1", + "global-modules": "2.0.0", + "globby": "8.0.2", + "gzip-size": "5.1.1", + "immer": "1.10.0", + "inquirer": "7.0.4", + "is-root": "2.1.0", + "loader-utils": "1.2.3", + "open": "^7.0.2", + "pkg-up": "3.1.0", + "react-error-overlay": "^6.0.6", + "recursive-readdir": "2.2.2", + "shell-quote": "1.7.2", + "strip-ansi": "6.0.0", + "text-table": "0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "browserslist": { + "version": "4.8.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz", + "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001023", + "electron-to-chromium": "^1.3.341", + "node-releases": "^1.1.47" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "inquirer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", + "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "react-dom": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", + "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-error-overlay": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.6.tgz", + "integrity": "sha512-Yzpno3enVzSrSCnnljmr4b/2KUQSMZaPuqmS26t9k4nW7uwJk6STWmH9heNjPuvqUTO3jOSPkHoKgO4+Dw7uIw==", + "dev": true + }, + "react-event-listener": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.5.10.tgz", + "integrity": "sha512-YZklRszh9hq3WP3bdNLjFwJcTCVe7qyTf5+LWNaHfZQaZrptsefDK2B5HHpOsEEaMHvjllUPr0+qIFVTSsurow==", + "requires": { + "@babel/runtime": "7.0.0-beta.42", + "fbjs": "^0.8.16", + "prop-types": "^15.6.0", + "warning": "^3.0.0" + } + }, + "react-infinite": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/react-infinite/-/react-infinite-0.13.0.tgz", + "integrity": "sha512-sISd4IYKELmOrvCq9i3FaQo4HR+Bn49ufK0eYAWQAisQ87QWJ5tqiQvEzww+JJZryZVMFvBCuiV7RUn/MfeEww==", + "requires": { + "enzyme-adapter-react-16": "1.1.1", + "lodash.isarray": "3.0.4", + "lodash.isfinite": "3.2.0", + "object-assign": "4.0.1" + }, + "dependencies": { + "object-assign": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", + "integrity": "sha1-mVBEVsNZi1ytT8WcJuipuxB/4L0=" + } + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-reconciler": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.7.0.tgz", + "integrity": "sha512-50JwZ3yNyMS8fchN+jjWEJOH3Oze7UmhxeoJLn2j6f3NjpfCRbcmih83XTWmzqtar/ivd5f7tvQhvvhism2fgg==", + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" + } + }, + "react-redux": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.1.tgz", + "integrity": "sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==", + "requires": { + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.1.0", + "invariant": "^2.2.4", + "loose-envify": "^1.1.0", + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", + "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "react-router": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", + "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "requires": { + "history": "^4.7.2", + "hoist-non-react-statics": "^2.5.0", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.1", + "warning": "^4.0.1" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "react-router-dom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", + "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "requires": { + "history": "^4.7.2", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.1", + "react-router": "^4.3.1", + "warning": "^4.0.1" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "react-scripts": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.0.tgz", + "integrity": "sha512-pBqaAroFoHnFAkuX+uSK9Th1uEh2GYdGY2IG1I9/7HmuEf+ls3lLCk1p2GFYRSrLMz6ieQR/SyN6TLIGK3hKRg==", + "dev": true, + "requires": { + "@babel/core": "7.8.4", + "@svgr/webpack": "4.3.3", + "@typescript-eslint/eslint-plugin": "^2.10.0", + "@typescript-eslint/parser": "^2.10.0", + "babel-eslint": "10.0.3", + "babel-jest": "^24.9.0", + "babel-loader": "8.0.6", + "babel-plugin-named-asset-import": "^0.3.6", + "babel-preset-react-app": "^9.1.1", + "camelcase": "^5.3.1", + "case-sensitive-paths-webpack-plugin": "2.3.0", + "css-loader": "3.4.2", + "dotenv": "8.2.0", + "dotenv-expand": "5.1.0", + "eslint": "^6.6.0", + "eslint-config-react-app": "^5.2.0", + "eslint-loader": "3.0.3", + "eslint-plugin-flowtype": "4.6.0", + "eslint-plugin-import": "2.20.0", + "eslint-plugin-jsx-a11y": "6.2.3", + "eslint-plugin-react": "7.18.0", + "eslint-plugin-react-hooks": "^1.6.1", + "file-loader": "4.3.0", + "fs-extra": "^8.1.0", + "fsevents": "2.1.2", + "html-webpack-plugin": "4.0.0-beta.11", + "identity-obj-proxy": "3.0.0", + "jest": "24.9.0", + "jest-environment-jsdom-fourteen": "1.0.1", + "jest-resolve": "24.9.0", + "jest-watch-typeahead": "0.4.2", + "mini-css-extract-plugin": "0.9.0", + "optimize-css-assets-webpack-plugin": "5.0.3", + "pnp-webpack-plugin": "1.6.0", + "postcss-flexbugs-fixes": "4.1.0", + "postcss-loader": "3.0.0", + "postcss-normalize": "8.0.1", + "postcss-preset-env": "6.7.0", + "postcss-safe-parser": "4.0.1", + "react-app-polyfill": "^1.0.6", + "react-dev-utils": "^10.2.0", + "resolve": "1.15.0", + "resolve-url-loader": "3.1.1", + "sass-loader": "8.0.2", + "semver": "6.3.0", + "style-loader": "0.23.1", + "terser-webpack-plugin": "2.3.4", + "ts-pnp": "1.1.5", + "url-loader": "2.3.0", + "webpack": "4.41.5", + "webpack-dev-server": "3.10.2", + "webpack-manifest-plugin": "2.2.0", + "workbox-webpack-plugin": "4.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "react-tap-event-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-tap-event-plugin/-/react-tap-event-plugin-2.0.1.tgz", + "integrity": "sha1-MWvrO8ZVbinshppyk+icgmqQdNI=", + "requires": { + "fbjs": "^0.8.6" + } + }, + "react-test-renderer": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-15.6.2.tgz", + "integrity": "sha1-0DM0NPwsQ4CSaWyncNpe1IA376g=", + "dev": true, + "requires": { + "fbjs": "^0.8.9", + "object-assign": "^4.1.0" + } + }, + "react-tiny-virtual-list": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-tiny-virtual-list/-/react-tiny-virtual-list-2.2.0.tgz", + "integrity": "sha512-MDiy2xyqfvkWrRiQNdHFdm36lfxmcLLKuYnUqcf9xIubML85cmYCgzBJrDsLNZ3uJQ5LEHH9BnxGKKSm8+C0Bw==", + "requires": { + "prop-types": "^15.5.7" + } + }, + "react-transition-group": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz", + "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==", + "requires": { + "chain-function": "^1.0.0", + "dom-helpers": "^3.2.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.5.6", + "warning": "^3.0.0" + } + }, + "react-virtualized": { + "version": "9.21.1", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.21.1.tgz", + "integrity": "sha512-E53vFjRRMCyUTEKuDLuGH1ld/9TFzjf/fFW816PE4HFXWZorESbSTYtiZz1oAjra0MminaUU1EnvUxoGuEFFPA==", + "requires": { + "babel-runtime": "^6.26.0", + "clsx": "^1.0.1", + "dom-helpers": "^2.4.0 || ^3.0.0", + "linear-layout-vector": "0.0.1", + "loose-envify": "^1.3.0", + "prop-types": "^15.6.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", + "requires": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "symbol-observable": "^1.0.4" + } + }, + "recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "dev": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "redux": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", + "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", + "requires": { + "lodash": "^4.2.1", + "lodash-es": "^4.2.1", + "loose-envify": "^1.1.0", + "symbol-observable": "^1.0.3" + } + }, + "redux-axios-middleware": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redux-axios-middleware/-/redux-axios-middleware-4.0.0.tgz", + "integrity": "sha1-gZUcPZrc5vg++JL9FWhXHOvkozY=" + }, + "redux-devtools-extension": { + "version": "2.13.8", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz", + "integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==", + "dev": true + }, + "redux-mock-store": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.3.tgz", + "integrity": "sha512-ryhkkb/4D4CUGpAV2ln1GOY/uh51aczjcRz9k2L2bPx/Xja3c5pSGJJPyR25GNVRXtKIExScdAgFdiXp68GmJA==", + "dev": true, + "requires": { + "lodash.isplainobject": "^4.0.6" + } + }, + "redux-persist": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-4.10.2.tgz", + "integrity": "sha512-U+e0ieMGC69Zr72929iJW40dEld7Mflh6mu0eJtVMLGfMq/aJqjxUM1hzyUWMR1VUyAEEdPHuQmeq5ti9krIgg==", + "requires": { + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.4", + "lodash-es": "^4.17.4" + } + }, + "reflect.ownkeys": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", + "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regex-parser": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.10.tgz", + "integrity": "sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + } + } + }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "request-promise-native": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "dev": true, + "requires": { + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-pathname": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "resolve-url-loader": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz", + "integrity": "sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "2.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.21", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "postcss": { + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "dev": true, + "requires": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", + "dev": true + } + } + }, + "rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sanitize.css": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz", + "integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg==", + "dev": true + }, + "sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "dependencies": { + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "requires": { + "xmlchars": "^2.1.1" + } + }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", + "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "dev": true, + "requires": { + "node-forge": "0.9.0" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "dev": true, + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "dev": true, + "requires": { + "is-buffer": "^1.0.2" + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", + "dev": true + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-assign": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz", + "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o=" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "sisteransi": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", + "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", + "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "dependencies": { + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", + "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", + "dev": true, + "requires": { + "babel-extract-comments": "^1.0.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-what": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", + "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==", + "dev": true + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz", + "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.4.tgz", + "integrity": "sha512-Nv96Nws2R2nrFOpbzF6IxRDpIkkIfmhvOws+IqMvYdFLO7o6wAILWFKONFgaYy8+T4LVz77DQW0f7wOeDEAjrg==", + "dev": true, + "requires": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.2.0", + "jest-worker": "^25.1.0", + "p-limit": "^2.2.2", + "schema-utils": "^2.6.4", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.4.3", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "find-cache-dir": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.0.tgz", + "integrity": "sha512-PtXtQb7IrD8O+h6Cq1dbpJH5NzD8+9keN1zZ0YlpDzl1PwXEJEBj6u1Xa92t1Hwluoozd9TNKul5Hi2iqpsWwg==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tiny-invariant": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz", + "integrity": "sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==" + }, + "tiny-warning": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz", + "integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "ts-pnp": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.5.tgz", + "integrity": "sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA==", + "dev": true + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.19", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", + "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + } + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-equal": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dev": true, + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "dependencies": { + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "webpack": { + "version": "4.41.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz", + "integrity": "sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.2.tgz", + "integrity": "sha512-pxZKPYb+n77UN8u9YxXT4IaIrGcNtijh/mi8TXbErHmczw0DtPnMTTjHj+eNjkqLOaAZM/qD7V59j/qJsEiaZA==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.2.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.6", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.25", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.4.0", + "spdy": "^4.0.1", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "fsevents": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-manifest-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", + "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", + "dev": true, + "requires": { + "fs-extra": "^7.0.0", + "lodash": ">=3.5 <5", + "object.entries": "^1.1.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "workbox-background-sync": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz", + "integrity": "sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-broadcast-update": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz", + "integrity": "sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-build": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-4.3.1.tgz", + "integrity": "sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.4", + "@hapi/joi": "^15.0.0", + "common-tags": "^1.8.0", + "fs-extra": "^4.0.2", + "glob": "^7.1.3", + "lodash.template": "^4.4.0", + "pretty-bytes": "^5.1.0", + "stringify-object": "^3.3.0", + "strip-comments": "^1.0.2", + "workbox-background-sync": "^4.3.1", + "workbox-broadcast-update": "^4.3.1", + "workbox-cacheable-response": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-expiration": "^4.3.1", + "workbox-google-analytics": "^4.3.1", + "workbox-navigation-preload": "^4.3.1", + "workbox-precaching": "^4.3.1", + "workbox-range-requests": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1", + "workbox-streams": "^4.3.1", + "workbox-sw": "^4.3.1", + "workbox-window": "^4.3.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", + "dev": true + } + } + }, + "workbox-cacheable-response": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz", + "integrity": "sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-4.3.1.tgz", + "integrity": "sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg==", + "dev": true + }, + "workbox-expiration": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-4.3.1.tgz", + "integrity": "sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-google-analytics": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz", + "integrity": "sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg==", + "dev": true, + "requires": { + "workbox-background-sync": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1" + } + }, + "workbox-navigation-preload": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz", + "integrity": "sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-precaching": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-4.3.1.tgz", + "integrity": "sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-range-requests": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz", + "integrity": "sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-routing": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-4.3.1.tgz", + "integrity": "sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-strategies": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-4.3.1.tgz", + "integrity": "sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-streams": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-4.3.1.tgz", + "integrity": "sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-sw": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-4.3.1.tgz", + "integrity": "sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w==", + "dev": true + }, + "workbox-webpack-plugin": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-4.3.1.tgz", + "integrity": "sha512-gJ9jd8Mb8wHLbRz9ZvGN57IAmknOipD3W4XNE/Lk/4lqs5Htw4WOQgakQy/o/4CoXQlMCYldaqUg+EJ35l9MEQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.0.0", + "json-stable-stringify": "^1.0.1", + "workbox-build": "^4.3.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", + "dev": true + } + } + }, + "workbox-window": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-4.3.1.tgz", + "integrity": "sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg==", + "dev": true, + "requires": { + "workbox-core": "^4.3.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "dev": true, + "requires": { + "microevent.ts": "~0.1.1" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", + "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.6.3" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", + "dev": true + } + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/viscoll-app/package.json b/viscoll-app/package.json new file mode 100644 index 00000000..e7c86488 --- /dev/null +++ b/viscoll-app/package.json @@ -0,0 +1,71 @@ +{ + "name": "viscoll-app", + "version": "0.1.0", + "private": true, + "dependencies": { + "axios": "^0.19.0", + "babel-polyfill": "^6.26.0", + "clientjs": "^0.1.11", + "copy-to-clipboard": "^3.2.0", + "es6-promise": "^4.1.0", + "file-saver": "^2.0.2", + "fuse.js": "^3.4.5", + "immutability-helper": "^2.4.0", + "js-file-download": "^0.4.7", + "jszip": "^3.2.1", + "localforage": "^1.5.0", + "lodash": "^4.17.15", + "material-ui": "^0.19.4", + "material-ui-chip-input": "^0.18.3", + "material-ui-superselectfield": "^1.5.6", + "openseadragon": "^2.3.1", + "paper": "^0.11.4", + "react": "^15.6.1", + "react-detect-offline": "^1.0.6", + "react-dom": "^15.6.1", + "react-redux": "^5.0.5", + "react-router-dom": "^4.1.1", + "react-tap-event-plugin": "^2.0.1", + "react-tiny-virtual-list": "^2.1.4", + "react-virtualized": "^9.21.1", + "redux": "^3.7.1", + "redux-axios-middleware": "^4.0.0", + "redux-persist": "^4.8.2" + }, + "devDependencies": { + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babel-preset-stage-2": "^6.24.1", + "enzyme": "^2.9.1", + "react-addons-test-utils": "^15.6.0", + "react-scripts": "^3.4.0", + "react-test-renderer": "^15.6.1", + "redux-devtools-extension": "^2.13.2", + "redux-mock-store": "^1.2.3", + "regenerator-runtime": "^0.11.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "eject": "react-scripts eject" + }, + "babel": { + "presets": [ + "react", + "es2015", + "stage-2" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/viscoll-app/public/favicon.ico b/viscoll-app/public/favicon.ico new file mode 100644 index 00000000..58182242 Binary files /dev/null and b/viscoll-app/public/favicon.ico differ diff --git a/viscoll-app/public/index.html b/viscoll-app/public/index.html new file mode 100644 index 00000000..61e09cd3 --- /dev/null +++ b/viscoll-app/public/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + Viscoll + + + +
+ + + diff --git a/viscoll-app/public/manifest.json b/viscoll-app/public/manifest.json new file mode 100644 index 00000000..be607e41 --- /dev/null +++ b/viscoll-app/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "192x192", + "type": "image/png" + } + ], + "start_url": "./index.html", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/viscoll-app/sass/components/_dialog.scss b/viscoll-app/sass/components/_dialog.scss new file mode 100644 index 00000000..5a588dff --- /dev/null +++ b/viscoll-app/sass/components/_dialog.scss @@ -0,0 +1,101 @@ +.addDialog { + + .title { + color: $black; + + } + h3 { + border-bottom: 1px solid #ddd; + } + + h4 { + color: $black; + margin-top: 2em; + margin-bottom: 0em; + font-weight:600; + } + + .label { + width: 200px; + display:inline-block; + } + .input { + width: 200px; + display:inline-block; + text-align: right; + } +} +.feedbackDialog { + p { + color: $black; + } + .label { + color: $black; + width: 100px; + display:inline-block; + vertical-align: top; + } + .input { + width: 250px; + display:inline-block; + text-align: right; + } +} +.newProjectDialog { + p { + color: $black; + } + h1 { + font-weight: normal; + text-transform: inherit; + } + h3 { + font-size: 1em; + margin-top: 0em; + } + .section { + margin-left: 1em; + } + .newProjectSelection { + button.btnSelection { + width: 100%; + background: $white; + border: 0; + @include box-shadow(0px 2px 10px 0px rgba(0,0,0,0.2)); + margin-bottom: 1em; + outline: 0; + + &:focus, &:hover { + outline-style: solid; + outline-width: 0.5em; + outline-color: transparentize($teal, 0.5); + cursor: pointer; + } + } + .selectItem { + display: flex; + padding: 2.5em 0em; + + .icon { + width: 50px; + padding:0px 15px; + } + .text { + line-height: 2.5em; + span:nth-child(1) { + text-align: left; + font-size: 2.5em; + display: block; + color: $black; + width: 100%; + } + span:nth-child(2) { + font-size: 1.3em; + color: lighten($black, 15); + width: 100%; + display: block; + } + } + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/components/_textarea.scss b/viscoll-app/sass/components/_textarea.scss new file mode 100644 index 00000000..a199c3ca --- /dev/null +++ b/viscoll-app/sass/components/_textarea.scss @@ -0,0 +1,8 @@ +textarea { + border: 1px solid #e0e0e0; + width: 100%; + font-size: 1em; + &:focus { + outline-color: $teal; + } +} \ No newline at end of file diff --git a/viscoll-app/sass/components/_tooltip.scss b/viscoll-app/sass/components/_tooltip.scss new file mode 100644 index 00000000..d8c70afd --- /dev/null +++ b/viscoll-app/sass/components/_tooltip.scss @@ -0,0 +1,67 @@ +.tooltip { + position: relative; + display: inline-block; + width: 100%; + + .text { + visibility: hidden; + width: 210px; + font-weight: 300; + background: transparentize(darken($black, 15%), 0.1); + color: #fff; + text-align: center; + @include border-radius(6px); + padding: 0.5em; + margin: 1em; + font-size: 0.9em; + opacity: 0; + @include transition(all, 200ms, ease-in-out); + + /* Position the tooltip */ + position: absolute; + z-index: 100; + + &::after { + content: " "; + position: absolute; + bottom: 100%; /* At the top of the tooltip */ + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent transparent transparentize(darken($black, 15%), 0.1) transparent; + } + } + + &.addDialog { + .text { + width: 70%; + &.active { + visibility: visible; + opacity: 1; + } + &::after { + left: 20%; + } + } + } + + &.eyeToggle { + width: initial; + .text { + left: -5%; + margin-left: 0; + width: 100px; + + &::after { + bottom: 100%; /* At the top of the tooltip */ + left: 15%; + margin-left: -5px; + } + &.active { + visibility: visible; + opacity: 1; + } + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/index.scss b/viscoll-app/sass/index.scss new file mode 100644 index 00000000..8a5a91ea --- /dev/null +++ b/viscoll-app/sass/index.scss @@ -0,0 +1,24 @@ +@import 'lib/variables'; +@import 'lib/mixins'; +@import 'layout/landing'; +@import 'layout/sidebar'; +@import 'layout/projectPanel'; +@import 'layout/infobox'; +@import 'layout/workspace'; +@import 'layout/tabular'; +@import 'layout/topbar'; +@import 'layout/notes'; +@import 'layout/filter'; +@import 'layout/loading'; +@import 'layout/404'; +@import 'layout/dashboard'; +@import 'layout/imageManager'; +@import 'layout/imageCollection'; +@import 'components/dialog'; +@import 'components/tooltip'; +@import 'components/textarea'; +@import 'typography'; + +html, body { + background: #F2F2F2; +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_404.scss b/viscoll-app/sass/layout/_404.scss new file mode 100644 index 00000000..b714f513 --- /dev/null +++ b/viscoll-app/sass/layout/_404.scss @@ -0,0 +1,25 @@ +.fourOhFour { + width: 100vw; + height: 100vh; + background: $bg_blue; + display: flex; + align-items: center; + .container { + width: 100vw; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: -10%; + h1 { + font-size: 8em; + color: $white; + padding-bottom: 0; + margin-bottom: 10px; + } + p { + color: $white; + margin-bottom: 30px; + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_dashboard.scss b/viscoll-app/sass/layout/_dashboard.scss new file mode 100644 index 00000000..6d9d4ce1 --- /dev/null +++ b/viscoll-app/sass/layout/_dashboard.scss @@ -0,0 +1,46 @@ +#listView { + .header { + display: flex; + padding: 1em 2em; + font-size: 0.8em; + div { + width: 50%; + } + } + button { + width: 100%; + display: flex; + text-align: left; + border-left: 1px solid transparentize($white, 0.5); + border-right: 1px solid transparentize($white, 0.5); + border-top: 1px solid transparentize($dark_gray, 0.8); + border-bottom: 1px solid transparentize($white, 0.5); + padding: 1em 2em; + background: transparentize($white, 0.5); + font-size: 0.9em; + color: $black; + cursor: pointer; + @include transition(background, 100ms, ease-in-out); + + &:hover { + background: $white; + } + &:focus { + outline-style: solid; + outline-color: transparentize($teal, 0.5); + outline-width: 2px; + border: 1px solid $teal; + } + &.selected { + background: $teal; + } + + &.selected:focus { + outline: 0; + } + + div { + width: 50%; + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_filter.scss b/viscoll-app/sass/layout/_filter.scss new file mode 100644 index 00000000..d6c3896c --- /dev/null +++ b/viscoll-app/sass/layout/_filter.scss @@ -0,0 +1,47 @@ +.filter { + width: 100%; + max-height: 45%; + position: relative; + z-index: 2; + left: 0; + display: flex; + justify-content: flex-end; + @include transition(opacity, 200ms, linear); +} +.filterContainer { + border-top:1px solid $gray; + position: fixed; + width: 82%; + max-height: 45%; + background: $white; + padding-bottom: 10px; + overflow: auto; + @include transition(top, 450ms, cubic-bezier(0.23, 1, 0.32, 1)); + @include box-shadow(0px 2px 8px 0px rgba(0,0,0,0.3)); +} +.filterRow { + display: flex; + align-items: flex-start; + justify-content: center; + & + .filterRow { + margin-top: -20px; + } + + .filterField { + width: 230px; + margin-left: 10px; + &:first-child { + margin-left:20px; + } + &:last-child { + width: 160px; + text-align: center; + } + } +} +.filterMessage { + text-transform: uppercase; + font-size: 0.9em; + font-weight: 500; + color: $dark_gray; +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_imageCollection.scss b/viscoll-app/sass/layout/_imageCollection.scss new file mode 100644 index 00000000..80deca51 --- /dev/null +++ b/viscoll-app/sass/layout/_imageCollection.scss @@ -0,0 +1,7 @@ +.imageFilter { + @include box-shadow(0px 2px 10px 0px rgba(0,0,0,0.1)); + background: $white; + margin: 0em 0em 0.5em 0em; + display: flex; + justify-content: space-between; +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_imageManager.scss b/viscoll-app/sass/layout/_imageManager.scss new file mode 100644 index 00000000..09528a89 --- /dev/null +++ b/viscoll-app/sass/layout/_imageManager.scss @@ -0,0 +1,229 @@ +.imageManager { + .form { + .row { + display: flex; + .label { + padding-top:1em; + min-width: 120px; + } + .input { + flex-grow: 1; + } + } + } + .manageManifests { + padding: 1em 2em 0em 2em; + h2 { + font-size: 1.1em; + padding: 0em 0em 0em 0em; + margin:0em; + color: $black; + } + .manifestCard { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + padding: 1.5em 1.5em 1em 1.5em; + + span { + font-size: 1em; + font-weight: normal; + color: transparentize($black, 0.2); + } + &>div { + flex-grow: 1; + } + .thumbnails { + text-align: right; + } + } + .addImages { + display: flex; + &>div { + width: 50%; + // border: 1px solid transparentize($dark_gray, 0.8); + padding: 1.5em 1.5em 1em 1.5em; + background: $white; + @include box-shadow(0px 2px 10px 0px rgba(0,0,0,0.1)); + + + &:first-child { + margin-right: 0.5em; + } + &:nth-child(2) { + margin-left: 0.5em; + } + } + } + } + .imageMapper { + .moveableItem { + height: 50px; + background: $white; + border-width: 0px 1px 0px 1px; + border-style: solid; + border-color: $gray; + cursor: pointer; + @include border-radius(3px); + padding-left: 0.5em; + display: flex; + justify-content: space-between; + align-items: center; + + .text { + color: $black; + padding-left: 0.5em; + @media screen and (max-width: $xsmall) { + font-size: 0.9em; + } + &>span { + font-size: 0.8em; + } + } + .thumbnail { + opacity: 0.5; + @include transition(all, 200ms, ease-in-out); + + &:hover { + opacity: 1; + cursor: pointer; + } + } + } + + .middleBar { + display: flex; + justify-content: space-around; + background: $white; + margin: 0.5em 1em; + padding: 0.2em 0em; + @media screen and (max-width: $small) { + margin: 0.5em 0em; + } + } + + .panelBar { + background: $white; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 2px solid $gray; + height: 40px; + .title { + padding-left: 1em; + text-transform: uppercase; + color: $black; + font-weight: bold; + } + .action { + padding-right: 0.5em; + } + } + .topPanel { + flex-grow: 2; + display: flex; + flex-direction: column; + width: 97%; + margin-top: 1em; + margin-left: 1em; + background: darken($gray, 3); + color: $black; + &>div { + // display: block; + width: 100%; + height: 100%; + margin: 0em; + // padding: 0.5em; + } + .boards { + display: flex; + width: 100%; + overflow-y: auto; + &>div { + width: 50%; + } + } + .binText { + text-transform: uppercase; + text-align: center; + display:flex; + align-items: center; + justify-content: center; + width: 100% !important; + height: 38vh; + } + } + .bottomPanel { + flex-grow: 2; + display: flex; + justify-content: space-between; + width: 97%; + // height: 42vh; + margin-left: 1em; + .backlog { + width: 49%; + padding: 0em; + + .scrollable { + overflow-y: auto; + } + } + .sideBacklog { + @extend .backlog; + .scrollable { + text-align: left; + height: 32vh; + } + } + .imageBacklog { + @extend .backlog; + height: 37vh; + .manifestSelection { + // background: teal; + height: 40px; + padding: 0em 1em; + display: flex; + justify-content: space-evenly; + border-bottom: 2px solid $gray; + background: $white; + width: 100%; + .title { + width:70px; + padding-top: 10px; + padding-right: 10px; + color: $black; + } + .form { + flex-grow: 1; + // background: green; + } + } + .scrollable { + height: 27vh; + } + } + } + .mainToolbar { + // position: fixed; + // bottom: 0; + margin-top: 0.5em; + width: 100%; + height: 50px; + display: flex; + justify-content: space-between; + align-items: center; + background: $white; + @include box-shadow(0px 2px 2px 0px rgba(0,0,0,0.05)); + .message { + padding-left: 1em; + text-transform: uppercase; + color: $black; + font-size: 0.9em; + } + .actions { + padding-right:1em; + } + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_infobox.scss b/viscoll-app/sass/layout/_infobox.scss new file mode 100644 index 00000000..51030c98 --- /dev/null +++ b/viscoll-app/sass/layout/_infobox.scss @@ -0,0 +1,51 @@ +.infoBox { + position: fixed; + display: inline-block; + width: 22%; + vertical-align:top; + right: 0; + top: 56px; + background: white; + max-height: 90%; + overflow-y: auto; + margin: 2% 2% 0% 0%; + @include box-shadow(0px 2px 10px 0px rgba(0,0,0,0.1)); + + .inner { + padding: 10px 20px 15px 20px; + @media screen and (max-width: $small) { + padding: 5px 15px 7px 15px; + } + @media screen and (max-width: $xsmall) { + padding: 5px 10px 7px 10px; + } + + .row { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + } + .label { + width: 35%; + } + .input { + width: 55%; + @media screen and (max-width: $xsmall) { + width: 50%; + } + } + } + button.image { + border: 0; + background: none; + padding: 0; + margin: 0; + font-size: 1em; + & + button { + margin-left: 0.5em; + } + overflow: none; + max-width: 40%; + } +} diff --git a/viscoll-app/sass/layout/_landing.scss b/viscoll-app/sass/layout/_landing.scss new file mode 100644 index 00000000..da1c9412 --- /dev/null +++ b/viscoll-app/sass/layout/_landing.scss @@ -0,0 +1,77 @@ +.landing { + width: 100vw; + height: 100vh; + background: $bg_blue2; + text-align: center; + + .container { + margin: 0 auto; + width: 80vw; + height: 90vh; + display: flex; + align-items: center; + align-content: center; + } + + img { + width: 100%; + } + + .panelLogo { + width: 55%; + } + + .panelLogin { + width: 40%; + padding-left: 5%; + } + + .panelBottom { + display: table; + width: 100%; + height:10vh; + background: $teal; + + div { + display: table-cell; + vertical-align: middle; + } + + span:first-child { + font-weight: bold; + } + + span { + color: #2b4352; + font-size: 1.1em; + display: block; + margin-bottom: .5em; + } + } + + hr { + border: 1px solid $teal; + } + + .spacingBottom { + margin-bottom: 1.5em; + } + + .spacingTop { + margin-top: 1.5em; + } + + p { + color: $teal; + } + + a { + color: $teal; + cursor: pointer; + text-decoration: underline; + + &:hover { + color: lighten($teal, 20%); + } + } +} diff --git a/viscoll-app/sass/layout/_loading.scss b/viscoll-app/sass/layout/_loading.scss new file mode 100644 index 00000000..abab09bd --- /dev/null +++ b/viscoll-app/sass/layout/_loading.scss @@ -0,0 +1,21 @@ +.appLoading { + width: 100vw; + height: 100vh; + background: $bg_blue; + display: flex; + align-items: center; + .container { + width: 100vw; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: -5%; + } + .logo { + width: 30%; + max-width: 300px; + margin-bottom: 1em; + } + +} diff --git a/viscoll-app/sass/layout/_notes.scss b/viscoll-app/sass/layout/_notes.scss new file mode 100644 index 00000000..3f935066 --- /dev/null +++ b/viscoll-app/sass/layout/_notes.scss @@ -0,0 +1,138 @@ +.notesManager { + height: 100%; + .container { + padding: 1em 2em 0em 2em; + } + + .browse { + height: 100%; + .notesList { + .item { + margin: 0.5em 0em 0em 0em; + overflow: hidden; + @include transition(background, 200ms, ease-in-out); + width: 90%; + font-size: 1em; + background: $gray; + border: 0px; + @include box-shadow(0px 0px 5px 0px rgba(0,0,0,0.2)); + &:hover { + cursor: pointer; + background: $white; + } + .title { + padding-top: 10px; + font-weight: 500; + color: $black; + } + .type { + padding-top: 5px; + padding-bottom: 10px; + color: $dark_gray; + font-style: italic; + font-size: 0.9em; + } + &.active { + background: $teal; + } + } + position: fixed; + top:56px; + left:18%; + width: 300px; + height: calc(100% - 56px); + overflow-y: auto; + text-align: center; + background: darken($gray, 5%); + } + .details { + position: relative; + width: calc(100% - 320px); + left: 300px; + } + } + .noteType { + padding: 1em 2em; + .items { + display: flex; + flex-wrap: wrap; + } + .item { + width: 220px; + margin-right: 1em; + } + .create { + display: flex; + margin-bottom: 2em; + .input { + margin-right: 1em; + } + } + } +} +.notesInfobox { + display: flex; + flex-wrap: wrap; +} +.noteSearch { + height: 56px; + @media screen and (max-width: 890px) { + width: 170px; + } + + .searchTextbox { + padding-top: 5px; + } +} +.searchOptions { + visibility: hidden; + opacity: 0; + background: $white; + width: 200px; + @include border-radius(0px 0px 6px 6px); + @include transition(all, 200ms, ease-in-out); + + @media screen and (max-width: 890px) { + width: 140px; + } + + padding: 0em 1em; + &.active{ + visibility: visible; + opacity: 1; + } +} +.noteForm { + width: 100%; + margin-left: 2%; + display: flex; + flex-wrap: wrap; + align-items: flex-start; + + .label { + padding-top:1em; + width: 20%; + @media screen and (max-width: $xsmall) { + font-size: 0.8em; + } + } + .input { + width: 80%; + @media screen and (max-width: $xsmall) { + padding-left: 5%; + width: 75%; + } + .textOnly { + margin-top: 1em; + color: $black; + } + } + .buttons { + text-align: right; + width: 100%; + padding-top: 2em; + } + .objectAttachments { + width: 100%; + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_projectPanel.scss b/viscoll-app/sass/layout/_projectPanel.scss new file mode 100644 index 00000000..dd3b71b7 --- /dev/null +++ b/viscoll-app/sass/layout/_projectPanel.scss @@ -0,0 +1,12 @@ +.projectPanelInfo { + padding: 1em; + line-height: 2em; + + .info { + padding-top: 1em; + font-size: 0.9em; + span { + color: transparentize($black, 0.2); + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_sidebar.scss b/viscoll-app/sass/layout/_sidebar.scss new file mode 100644 index 00000000..c7a87440 --- /dev/null +++ b/viscoll-app/sass/layout/_sidebar.scss @@ -0,0 +1,152 @@ +.sidebar { + position: fixed; + display: block; + top: 55px; + width: 18%; + height: calc(100% - 55px); + background: $bg_blue; + opacity: 1; + @include transition(all, 200ms, ease-in-out); + overflow-y: auto; + z-index: 2200; + + &.lowerZIndex { + z-index: inherit; + } + + &.hidden { + opacity: 0; + } + hr { + border: 1px solid $teal; + margin: 0; + } + h1 { + text-transform: uppercase; + color: $white; + font-size: 1em; + font-weight: 600; + } + h2 { + color: $white; + font-size: 0.8em; + font-weight: lighter; + padding: 1em 0em; + margin: 0em; + text-transform: uppercase; + &:nth-child(1) { + padding-top: 0em; + } + @media screen and (max-width: $xsmall) { + font-size: 0.7em; + } + } + .panel { + .header { + padding: 0.5em 1em; + display: flex; + justify-content: space-between; + @media screen and (max-width: $xsmall) { + font-size: 0.85em; + } + } + .content { + padding: 1em; + background: transparentize($fg_blue, 0.8); + &.hidden { + display: none; + } + } + &:last-child { + margin-bottom: 50px; + } + } + .selectMode { + padding: 1em 1em 1em 1em; + text-align: center; + + span { + font-size: 13px; + color: $teal; + text-transform: uppercase; + } + .tip { + font-size: 0.8em; + color: $gray; + text-align: left; + line-height: 1.5em; + } + .close { + text-align: right; + margin-right: -10px; + margin-top: -10px; + } + background: darken($bg_blue, 3%); + } + + .navigation { + text-align: center; + margin: 0px; + text-transform: uppercase; + @include transition(all, 100ms, ease-in-out); + width: 100%; + border: 0; + background: none; + color: $white; + font-size: 1em; + &:hover { + background: transparentize($teal,0.90); + font-weight: bold; + } + &.active { + background: $teal; + font-weight: bold; + color: $black; + } + } + .manager { + @extend .navigation; + padding: 0.5em 0em; + @media screen and (max-width: $xsmall) { + font-size: 0.8em; + } + } + .dashboard { + @extend .navigation; + text-transform: none; + padding: 0.75em 0em; + &:hover { + background: transparentize($white,0.95); + } + &.active { + background: transparentize($white, 0.9); + font-weight: bold; + color: $white; + } + @media screen and (max-width: $xsmall) { + font-size: 0.8em; + } + } + + .export { + line-height: 45px; + } +} + +.feedback { + position: fixed; + bottom: 0; + left: 0; + width: 18%; + z-index:10000; + text-align: center; +} + +.editIcon { + @include transition(all, 200ms, ease-in-out); + background: #BABABA !important; + &:hover { + background: #A5A5A5 !important; + cursor: pointer; + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_tabular.scss b/viscoll-app/sass/layout/_tabular.scss new file mode 100644 index 00000000..ee25668e --- /dev/null +++ b/viscoll-app/sass/layout/_tabular.scss @@ -0,0 +1,227 @@ +.groupContainer { + input { + opacity: 0; + width: 1px; + height: 1px; + margin-top: -10px; + float:right; + + } + @include box-shadow(0px 2px 10px 0px rgba(0,0,0,0.1)); + @include transition(border, 150ms, ease-in-out); + background: $white; + margin-bottom: 1em; + cursor: pointer; + border: 2px solid $white; + + &.focus { + border: 2px solid $teal; + } + &.active { + background: $teal; + } + + .groupMembers { + padding: 0em 1em 1em 1em; + + &.hidden { + display: none; + } + } +} + +.itemContainer { + display: flex; + align-items: stretch; + &.group { + justify-content: space-between; + + .groupSection { + display: flex; + } + + .toggleButton { + float: right; + } + } +} +.leafContainer { + @include box-shadow(0px 2px 10px 0px rgba(0,0,0,0.1)); + margin-bottom: 0.2em; + cursor: pointer; + background: $white; + + .leafSection { + display: flex; + flex-grow: 1; + @include transition(border, 100ms, ease-in-out); + border: 2px solid $white; + + &.active { + background: $teal; + } + + &.focus { + border: 2px solid $teal; + } + } +} + +.itemName { + width: 70px; + display: flex; + align-items: center; + font-weight: 500; + padding-left: 20px; + min-height: 45px; + @media screen and (max-width:$xsmall) { + font-size: 0.9em; + } +} +.itemAttributes { + flex-grow: 4; + display: flex; +} +.attribute { + display: flex; + align-items: center; + max-width: 100px; + padding: 0.5em 0.8em; + color: transparentize($black, 0.4); + font-weight: 400; + border-left: 1px solid transparentize($black, 0.95); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + @media screen and (max-width:$xsmall) { + font-size: 0.9em; + } + + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 11px; + @media screen and (max-width: $xsmall) { + font-size: 10px; + } + } + + span:nth-child(1) { + color: transparentize($black, 0.6); + display: block; + } + + &.active, &:hover { + color: $black; + } + + &.active { + &.small { + color: $black; + } + span:nth-child(1) { + color: transparentize($black, 0.3); + } + } +} + +.sideSection { + flex-grow: 1; + display: flex; + flex-direction: column; + border-left: 1px solid transparentize($black, 0.85); + overflow: hidden; + + .side { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + border: 2px solid $white; + cursor: pointer; + + &.active { + background: $teal; + } + &.focus { + border: 2px solid $teal; + color: transparentize($black, 0); + } + + .name { + width: 40px; + display: inline-block; + padding: 7px 10px 0px 10px; + vertical-align: top; + font-weight: normal; + border-right: 1px solid transparentize($black, 0.95); + } + .attribute { + display: inline-block; + vertical-align: top; + padding: 0px 10px; + padding-top: 2px; + border-right: 1px solid transparentize($black, 0.95); + color: transparentize($black, 0.4); + @include transition(color, 200ms, ease-in-out); + font-size: 13px; + + span { + font-weight: normal; + color: transparentize($black, 0.4); + } + &:hover { + color: transparentize($black, 0); + } + &.active { + color: transparentize($black, 0); + } + } + &:first-child { + border-bottom: 2px solid transparentize($black, 0.90); + &.focus { + border-bottom: 2px solid $teal; + } + } + } +} + +.sideToggle { + width: 20px; + height: 49px; + border-left: 1px solid transparentize($black, 0.85); + cursor: pointer; + .side { + display: block; + width: 16px; + height: 18px; + padding-top: 3px; + text-align: center; + color: transparentize($black, 0.4); + border: 2px solid $white; + + &:first-child { + border-bottom: 1px solid transparentize($black, 0.85); + } + &.active { + background: $teal; + } + &.focus { + border: 2px solid $teal; + color: transparentize($black, 0); + } + } +} + +.flash { + animation-name: flashify; + animation-duration: 3s; +} + +@include keyframes(flashify) { + 0% { border: 2px solid rgba(255, 255, 255, 1); } + 35% { border: 2px solid rgba(78, 214, 203, 1); } + // 50% { border: 2px solid rgba(255,255,255, 1); } + 80% { border: 2px solid rgba(78, 214, 203, 1); } + 100% { border: 2px solid rgba(255, 255, 255, 1); } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_topbar.scss b/viscoll-app/sass/layout/_topbar.scss new file mode 100644 index 00000000..74424f3c --- /dev/null +++ b/viscoll-app/sass/layout/_topbar.scss @@ -0,0 +1,32 @@ +.topbar { + position: fixed; + left:0px; + width: 100%; + z-index: 2200; + @include box-shadow(0px 1px 2px 1px rgba(0,0,0,0.05)); + + &.lowerZIndex { + z-index: 1500; + } + + .logo { + float:left; + width: 18%; + height: 55px; + text-align: center; + position: relative; + background: $bg_blue; + img { + width: 60%; + margin: 0; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + @media screen and (max-width: $xsmall) { + width: 85%; + } + } + } +} \ No newline at end of file diff --git a/viscoll-app/sass/layout/_workspace.scss b/viscoll-app/sass/layout/_workspace.scss new file mode 100644 index 00000000..66a111ac --- /dev/null +++ b/viscoll-app/sass/layout/_workspace.scss @@ -0,0 +1,33 @@ +.workspace { + position: absolute; + left: 18%; + top: 56px; +} +.projectWorkspace { + @extend .workspace; + width: 54%; + margin: 2%; + .viewingMode { + display: flex; + &>div:first-child { + margin-top: 4px; + } + &>div:nth-child(2) { + margin-top: 14px; + + } + } +} +.dashboardWorkspace { + @extend .workspace; + width: 82%; + @include transition(all, 450ms, cubic-bezier(0.23, 1, 0.32, 1)); + + &.projectPanelOpen { + width: 70%; + } +} +.notesWorkspace, .imageWorkspace { + @extend .workspace; + width: 82%; +} \ No newline at end of file diff --git a/viscoll-app/sass/lib/_mixins.scss b/viscoll-app/sass/lib/_mixins.scss new file mode 100644 index 00000000..470684c1 --- /dev/null +++ b/viscoll-app/sass/lib/_mixins.scss @@ -0,0 +1,53 @@ +@mixin border-radius($radius) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + -ms-border-radius: $radius; + border-radius: $radius; +} + +@mixin box-shadow($shadow...) { + -webkit-box-shadow: $shadow; + box-shadow: $shadow; +} + +@mixin transition($target, $duration, $transitionType) { + -webkit-transition: $target $duration $transitionType; + -ms-transition: $target $duration $transitionType; + transition: $target $duration $transitionType; +} + +@mixin breakpoint($point) { + @media(nth($point, 1): nth($point, 2)) { @content; } +} + +@mixin centerize($x_percent, $y_percent) { + position: relative; + top: $y_percent; + left: $x_percent; + transform: translateY(-$y_percent) translateX(-$x_percent); +} + +@mixin user-select($value) { +-webkit-touch-callout: $value; /* iOS Safari */ + -webkit-user-select: $value; /* Safari */ + -khtml-user-select: $value; /* Konqueror HTML */ + -moz-user-select: $value; /* Firefox */ + -ms-user-select: $value; /* Internet Explorer/Edge */ + user-select: $value; /* Non-prefixed version, currently + supported by Chrome and Opera */ +} + +@mixin keyframes($name) { + @-webkit-keyframes #{$name} { + @content; + } + @-moz-keyframes #{$name} { + @content; + } + @-ms-keyframes #{$name} { + @content; + } + @keyframes #{$name} { + @content; + } +} \ No newline at end of file diff --git a/viscoll-app/sass/lib/_variables.scss b/viscoll-app/sass/lib/_variables.scss new file mode 100644 index 00000000..e45ef30e --- /dev/null +++ b/viscoll-app/sass/lib/_variables.scss @@ -0,0 +1,17 @@ +// Palette +$fg_blue: #526C91; +$bg_blue: #3A4B55; +$bg_blue2: #2B4352; +$teal: #4ED6CB; +$white: #FFFFFF; +$gray: #F2F2F2; +$dark_gray: #727272; +$black: #4e4e4e; +$error: #bd4a4a; +$success: #34A251; +$warning: #E37A05; + +// Breakpoints +$medium: 1200px; +$small: 1024px; +$xsmall: 768px; diff --git a/viscoll-app/sass/typography.scss b/viscoll-app/sass/typography.scss new file mode 100644 index 00000000..f395be97 --- /dev/null +++ b/viscoll-app/sass/typography.scss @@ -0,0 +1,21 @@ +h1 { + font-size: 1.6em; + font-weight: bold; + color: $black; + @media screen and (max-width: $xsmall) { + font-size: 1.3em; + } +} +h2 { + color: $dark_gray; + padding-top: 0.8em; + font-size: 1.15em; + @media screen and (max-width: $xsmall) { + font-size: 1em; + } +} +h3 { + @media screen and (max-width: $xsmall) { + font-size: 1em; + } +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/filterActions.js b/viscoll-app/src/actions/backend/filterActions.js new file mode 100644 index 00000000..b39642a1 --- /dev/null +++ b/viscoll-app/src/actions/backend/filterActions.js @@ -0,0 +1,110 @@ + +export function filterProject(projectID, queries) { + /** + "queries": [ + { + "type": "Leaf", + "attribute": "material", + "condition": "equals", + "values": ["paper"], + "conjunction": "AND" + } + ] + */ + return { + types: ['SHOW_LOADING','FILTER_PROJECT_SUCCESS','FILTER_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/filter`, + method: 'put', + data: queries, + successMessage: "Successfully filtered the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function reapplyFilterProject(projectID, filters) { + const { queries, active } = filters; + if (!active) + return {type: "NO_FILTER_CHANGE"} + let index = 0; + let haveErrors = false; + for (let query of queries) { + if (query.type === null) + haveErrors = true + if (query.attribute === "") + haveErrors = true + if (query.values.length === 0) + haveErrors = true + if (query.condition === "") + haveErrors = true + if (index !== queries.length-1) + if (query.conjunction === "") + haveErrors = true + index += 1; + } + if (!haveErrors){ + return { + types: ['NO_LOADING','FILTER_PROJECT_SUCCESS','FILTER_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/filter`, + method: 'put', + data: {queries}, + successMessage: "" , + errorMessage: "Ooops! Something went wrong" + } + } + }; + } + return { type: "NO_FILTER_CHANGE" } +} + + +export function resetFilters(queries) { + return { + type: 'RESET_FILTERS', + payload: queries, + }; +} + + +export function toggleFilterDisplay() { + return { + type: 'TOGGLE_FILTER_DISPLAY' + }; +} + + +export function updateFilterQuery(newQueries) { + return { + type: 'UPDATE_FILTER_QUERY', + payload: newQueries + }; +} + + +export function updateFilterSelection(selection, matchingFilterObjects, allObjects) { + let type = selection.split("_")[0]; + const select = selection.split("_")[1]; + let selectedObjects = { type: type? type.slice(0,-1) : type, members: [], lastSelected: "" }; + if (select==="all"){ + selectedObjects.members = allObjects[type.slice(0, type.length-1)+"IDs"]; + } else if (select==="matching") { + selectedObjects.members = allObjects[type.slice(0, type.length-1)+"IDs"].filter((id) => { + if (type==="Rectos" || type==="Versos") + return matchingFilterObjects.Sides.includes(id) + else + return matchingFilterObjects[type].includes(id) + }) + } + return { + type: 'UPDATE_FILTER_SELECTION', + payload: { + selection, + selectedObjects + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/groupActions.js b/viscoll-app/src/actions/backend/groupActions.js new file mode 100644 index 00000000..a74d0daf --- /dev/null +++ b/viscoll-app/src/actions/backend/groupActions.js @@ -0,0 +1,79 @@ + +export function addGroups(group, additional) { + return { + types: ['CREATE_GROUPS_FRONTEND','CREATE_GROUPS_SUCCESS_BACKEND','CREATE_GROUPS_FAILED_BACKEND'], + payload: { + request : { + url: `/groups`, + method: 'post', + data: {group, additional}, + successMessage: "Successfully added the groups" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function updateGroup(groupID, group) { + return { + types: ['UPDATE_GROUP_FRONTEND','UPDATE_GROUP_SUCCESS_BACKEND','UPDATE_GROUP_FAILED_BACKEND'], + payload: { + request : { + url: `/groups/${groupID}`, + method: 'put', + data: {group}, + successMessage: "Successfully updated the group" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function updateGroups(groups) { + return { + types: ['UPDATE_GROUPS_FRONTEND','UPDATE_GROUPS_SUCCESS_BACKEND','UPDATE_GROUPS_FAILED_BACKEND'], + payload: { + request : { + url: `/groups`, + method: 'put', + data: {groups}, + successMessage: "Successfully updated the groups" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function deleteGroup(groupID) { + return { + types: ['DELETE_GROUP_FRONTEND','DELETE_GROUP_SUCCESS_BACKEND','DELETE_GROUP_FAILED_BACKEND'], + payload: { + request : { + url: `/groups/${groupID}`, + method: 'delete', + successMessage: "Successfully deleted the group" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function deleteGroups(groups, projectID) { + return { + types: ['DELETE_GROUPS_FRONTEND','DELETE_GROUPS_SUCCESS_BACKEND','DELETE_GROUPS_FAILED_BACKEND'], + payload: { + request : { + url: `/groups`, + method: 'delete', + data: {...groups, projectID}, + successMessage: "Successfully deleted the groups" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} diff --git a/viscoll-app/src/actions/backend/imageActions.js b/viscoll-app/src/actions/backend/imageActions.js new file mode 100644 index 00000000..ba26353c --- /dev/null +++ b/viscoll-app/src/actions/backend/imageActions.js @@ -0,0 +1,98 @@ + +/** + * + * @param {list} projectIDs [ 5a26d22f3b0eb7594c9dec23, ... ] + * @param {imageIDs} imageIDs [ 5a26d22f3b0eb7594c9dec23, ... ] + */ +export function linkImages(projectIDs, imageIDs) { + return { + types: ['LINK_IMAGES_FRONTEND','LINK_IMAGES_SUCCESS_BACKEND','LINK_IMAGE_FAILED_BACKEND'], + payload: { + request : { + url: `/images/link`, + data: {projectIDs, imageIDs}, + method: 'put', + successMessage: projectIDs.length>1 ? "You have successfully linked the projects to this image" : "You have successfully linked the project to this image" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +/** + * + * @param {list} projectIDs [ 5a26d22f3b0eb7594c9dec23, ... ] + * @param {imageIDs} imageIDs [ 5a26d22f3b0eb7594c9dec23, ... ] + */ +export function unlinkImages(projectIDs, imageIDs) { + return { + types: ['UNLINK_IMAGES_FRONTEND','UNLINK_IMAGES_SUCCESS_BACKEND','UNLINK_IMAGES_FAILED_BACKEND'], + payload: { + request : { + url: `/images/unlink`, + data: {projectIDs, imageIDs}, + method: 'put', + successMessage: projectIDs.length>1 ? "You have successfully unlinked the projects to this image" : "You have successfully unlinked this project to this image" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +/** + * + * @param {list} imageIDs + */ +export function deleteImages(imageIDs) { + return { + types: ['DELETE_IMAGES_FRONTEND','DELETE_IMAGES_SUCCESS_BACKEND','DELETE_IMAGES_FAILED_BACKEND'], + payload: { + request : { + url: `/images/`, + method: 'delete', + data: {imageIDs}, + successMessage: imageIDs.length>1?"You have successfully deleted the images":"You have successfully deleted the image", + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +/** + * + * @param {list} images [ { filename:"", contents: data(base64) } , ... ] + */ +export function uploadImages(images, projectID) { + return { + types: ['SHOW_LOADING','UPLOAD_IMAGES_SUCCESS_BACKEND','UPLOAD_IMAGES_FAILED_BACKEND'], + payload: { + request : { + url: `/images`, + method: 'post', + data: {projectID, images}, + successMessage: images.length>1? "You have successfully uploaded your images" : "You have successfully uploaded your image" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function mapSidesToImages(sideMappings) { + // sideMappings = [{id: 112, image: {}}, ...] + return { + types: ['MAP_SIDES_FRONTEND','MAP_SIDES_SUCCESS_BACKEND','MAP_SIDES_FAILED_BACKEND'], + payload: { + request : { + url: `/sides`, + method: 'put', + data: {sides: sideMappings}, + successMessage: "Successfully updated the sides" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/interactionActions.js b/viscoll-app/src/actions/backend/interactionActions.js new file mode 100644 index 00000000..b9410874 --- /dev/null +++ b/viscoll-app/src/actions/backend/interactionActions.js @@ -0,0 +1,165 @@ +export function changeViewMode(viewMode) { + return { + type: "CHANGE_VIEW_MODE", + payload: viewMode + }; +} + +export function changeManagerMode(managerMode) { + return { + type: "CHANGE_MANAGER_MODE", + payload: managerMode + }; +} + +export function changeNotesTab(newTab) { + return { + type: "CHANGE_NOTES_TAB", + payload: newTab + }; +} + +export function changeImageTab(newTab) { + return { + type: "CHANGE_IMAGES_TAB", + payload: newTab + }; +} + +export function toggleFilterPanel(value) { + return { + type: "TOGGLE_FILTER_PANEL", + payload: value + }; +} + +export function handleObjectPress(selectedObjects, object, event) { + selectedObjects = {...selectedObjects, members: [...selectedObjects.members]}; + selectedObjects.type = object.memberType; + selectedObjects.members = [object.id]; + + return { + type: "TOGGLE_SELECTED_OBJECTS", + payload: selectedObjects + }; + +} + +export function handleObjectClick(selectedObjects, object, event, objects) { + selectedObjects = {...selectedObjects, members: [...selectedObjects.members]}; + if (event.ctrlKey || event.metaKey || (event.modifiers!==undefined && event.modifiers.command)) { + // Toggle this object without clearing active objects unless type is different + if (selectedObjects.type !== object.memberType) { + selectedObjects.members = []; + selectedObjects.type = object.memberType; + } + const index = selectedObjects.members.indexOf(object.id); + if (index !== -1) { + selectedObjects.members.splice(index, 1); + } + else { + selectedObjects.members.push(object.id) + } + } + if (event.button === 0 || event.modifiers!==undefined) { + let notCtrl=event.ctrlKey !== undefined && !event.ctrlKey && !event.shiftKey; + let notCmd=event.metaKey !== undefined && !event.metaKey && !event.shiftKey; + let notCanvasCmd=event.modifiers !== undefined && !event.modifiers.command && !event.modifiers.shift; + if ((notCtrl&¬Cmd)||notCanvasCmd) { + // Clear all and toggle only this object + if (selectedObjects.members.includes(object.id)) { + selectedObjects.members = []; + } + else { + selectedObjects.members = [object.id]; + } + } + if (event.shiftKey || (event.modifiers!==undefined && event.modifiers.shift)) { + window.getSelection().removeAllRanges(); + // Object type changed, clear all active selected objects + if (selectedObjects.type !== object.memberType) { + selectedObjects.members = [object.id]; + } else { + // Select all similar type objects within this object and last selected object + const orderOfCurrentElement = objects[object.memberType+"s"].indexOf(object.id) + const orderOfLastElement = objects[object.memberType+"s"].indexOf(selectedObjects.lastSelected) + let indexes = [orderOfLastElement, orderOfCurrentElement]; + indexes.sort((a, b) => {return a-b}); + const currentSelected = [...selectedObjects.members]; + selectedObjects.members = objects[object.memberType+"s"].slice(indexes[0], indexes[1]+1); + for (let id of currentSelected){ + if (!selectedObjects.members.includes(id)) + selectedObjects.members.push(id); + } + } + } + } + // Sort the selected members by ascending order + selectedObjects.members.sort((a, b)=>objects[object.memberType+"s"].indexOf(a) > objects[object.memberType+"s"].indexOf(b) ? 1 : -1); + + if (selectedObjects.members.length === 0) { + selectedObjects.type = ""; + } else { + selectedObjects.type = object.memberType; + selectedObjects.lastSelected = object.id; + } + + return { + type: "TOGGLE_SELECTED_OBJECTS", + payload: selectedObjects + }; +} + +/** + * Switch selectedObjects between types: Leaf, Recto, Verso + * @param {object} selectedObjects currently selected objects + * @param {string} newType new type to switch to (leaf, recto or verso) + * @param {object} Leafs all Leaf, Recto & Verso objects of this project + */ +export function changeInfoBoxTab(newType, selectedObjects, objects) { + let newSelectedObjects = {type: newType, members: [], lastSelected: ""}; + if (selectedObjects.type==="newType") + return { type: "NO_TAB_CHANGE" } + + const object = objects[selectedObjects.type+"s"]; + + switch(newType) { + case "Leaf": + for (let id of selectedObjects.members){ + newSelectedObjects.members.push(object[id].parentID) + } + break; + case "Recto": + for (let id of selectedObjects.members){ + if (selectedObjects.type==="Leaf") + newSelectedObjects.members.push(object[id].rectoID) + else + newSelectedObjects.members.push(objects.Leafs[object[id].parentID].rectoID) + } + break; + case "Verso": + for (let id of selectedObjects.members){ + if (selectedObjects.type==="Leaf") + newSelectedObjects.members.push(object[id].versoID) + else + newSelectedObjects.members.push(objects.Leafs[object[id].parentID].versoID) + } + break; + default: + break; + } + + newSelectedObjects.lastSelected = newSelectedObjects.members[newSelectedObjects.members.length-1] + + return { + type: "TOGGLE_SELECTED_OBJECTS", + payload: newSelectedObjects, + }; +} + +export function toggleVisualizationDrawing(request) { + return { + type: 'TOGGLE_VISUALIZATION_DRAWING', + payload: request + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/leafActions.js b/viscoll-app/src/actions/backend/leafActions.js new file mode 100644 index 00000000..11c043e8 --- /dev/null +++ b/viscoll-app/src/actions/backend/leafActions.js @@ -0,0 +1,141 @@ + +export function addLeafs(leaf, additional) { + let successMessage = "Successfully added the leaf"; + if (additional.noOfLeafs>1) successMessage = "Successfully added the leaves"; + + return { + types: ['CREATE_LEAVES_FRONTEND','CREATE_LEAVES_SUCCESS_BACKEND','CREATE_LEAVES_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs`, + method: 'post', + data: { leaf, additional }, + successMessage, + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function updateLeaf(leafID, leaf) { + return { + types: ['UPDATE_LEAF_FRONTEND','UPDATE_LEAF_SUCCESS_BACKEND','UPDATE_LEAF_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs/${leafID}`, + method: 'put', + data: {leaf}, + successMessage: "Successfully updated the leaf" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function updateLeafs(leafs, project_id) { + return { + types: ['UPDATE_LEAVES_FRONTEND','UPDATE_LEAVES_SUCCESS_BACKEND','UPDATE_LEAVES_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs`, + method: 'put', + data: {leafs, project_id}, + successMessage: "Successfully updated the leaves" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + +/** + leafs: [ + "59ea025c3b0eb7168e9591de", + "59ea025c3b0eb7168e9591de", + "59ea025c3b0eb7168e9591de", + "59ea025c3b0eb7168e9591de" + ] + */ +export function autoConjoinLeafs(leafs) { + return { + types: ['AUTOCONJOIN_LEAFS_FRONTEND','AUTOCONJOIN_LEAFS_SUCCESS_BACKEND','AUTOCONJOIN_LEAFS_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs/conjoin`, + method: 'put', + data: {leafs}, + successMessage: "Successfully conjoined the leaves" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + + +export function deleteLeaf(leafID) { + return { + types: ['DELETE_LEAF_FRONTEND','DELETE_LEAF_SUCCESS_BACKEND','DELETE_LEAF_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs/${leafID}`, + method: 'delete', + successMessage: "Successfully deleted the leaf" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function deleteLeafs(leafs) { + return { + types: ['DELETE_LEAVES_FRONTEND','DELETE_LEAFS_SUCCESS_BACKEND','DELETE_LEAFS_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs`, + method: 'delete', + data: leafs, + successMessage: "Successfully deleted the leaves" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function generateFolioNumbers(startNumber, rectoIDs, versoIDs) { + return { + types: ['GENERATE_FOLIO_NUMBERS_FRONTEND','GENERATE_FOLIO_NUMBERS_SUCCESS_BACKEND','GENERATE_FOLIO_NUMBERS_FAILED_BACKEND'], + payload: { + request : { + url: `/sides/generateFolio`, + method: 'put', + data: {startNumber, rectoIDs, versoIDs}, + successMessage: "Successfully generated the folio numbers" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function generatePageNumbers(startNumber, rectoIDs, versoIDs) { + return { + types: ['GENERATE_PAGE_NUMBERS_FRONTEND','GENERATE_PAGE_NUMBERS_SUCCESS_BACKEND','GENERATE_PAGE_NUMBERS_FAILED_BACKEND'], + payload: { + request : { + url: `/sides/generatePageNumber`, + method: 'put', + data: {startNumber, rectoIDs, versoIDs}, + successMessage: "Successfully generated the page numbers" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/manifestActions.js b/viscoll-app/src/actions/backend/manifestActions.js new file mode 100644 index 00000000..c81e0e02 --- /dev/null +++ b/viscoll-app/src/actions/backend/manifestActions.js @@ -0,0 +1,70 @@ + + + +export function createManifest(projectID, manifest) { + return { + types: ['SHOW_LOADING','CREATE_MANIFEST_SUCCESS','CREATE_MANIFEST_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/manifests`, + method: 'post', + data: manifest, + successMessage: "You have successfully created the manifest" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function updateManifest(projectID, manifest) { + return { + types: ['UPDATE_MANIFEST_FRONTEND','UPDATE_MANIFEST_SUCCESS_BACKEND','UPDATE_MANIFEST_FAILED_BACKEND'], + payload: { + request : { + url: `/projects/${projectID}/manifests`, + method: 'put', + data: manifest, + successMessage: "You have successfully updated the manifest" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + +export function deleteManifest(projectID, manifest) { + return { + types: ['DELETE_MANIFEST_FRONTEND','DELETE_MANIFEST_SUCCESS_BACKEND','DELETE_MANIFEST_FAILED_BACKEND'], + payload: { + request : { + url: `/projects/${projectID}/manifests`, + method: 'delete', + data: manifest, + successMessage: "You have successfully deleted the manifest" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function cancelCreateManifest(){ + return {type: "CANCEL_CREATE_MANIFEST"} +} + + +export function cloneProjectImagesMapping(projectID) { + return { + types: ['NO_LOADING','CLONE_PROJECT_MAPPING_SUCCESS','CLONE_PROJECT_MAPPING_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/cloneImageMapping`, + method: 'get', + successMessage: "" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/noteActions.js b/viscoll-app/src/actions/backend/noteActions.js new file mode 100644 index 00000000..d84232ef --- /dev/null +++ b/viscoll-app/src/actions/backend/noteActions.js @@ -0,0 +1,183 @@ + +export function addNote(note) { + /** + note: { + "project_id": "5951303fc9bf3c7b9a573a3f", + "id": "595130sadsadsa9bf3c7b9a573a3f" + "title": "some title for note", + "type": "Ink", + "description": "blue ink", + "show": "true" + } + */ + return { + types: ['CREATE_NOTE_FRONTEND','CREATE_NOTE_SUCCESS_BACKEND','CREATE_NOTE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes`, + method: 'post', + data: {note}, + successMessage: "Successfully created the note" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateNote(noteID, note) { + /** + note: { + "title": "some title for note", + "type": "Ink", + "description": "blue ink" + } + */ + return { + types: ['UPDATE_NOTE_FRONTEND','UPDATE_NOTE_SUCCESS_BACKEND','UPDATE_NOTE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/${noteID}`, + method: 'put', + data: {note}, + successMessage: "Successfully updated the note" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + + +export function deleteNote(noteID) { + return { + types: ['DELETE_NOTE_FRONTEND','DELETE_NOTE_SUCCESS_BACKEND','DELETE_NOTE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/${noteID}`, + method: 'delete', + successMessage: "Successfully deleted the note" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + +export function linkNote(noteID, objects) { + /** + objects: [ + { + "id": "5951303fc9bf3c7b9a573a3f", + "type": "Group" + }, + ] + */ + return { + types: ['LINK_NOTE_FRONTEND','LINK_NOTE_SUCCESS_BACKEND','LINK_NOTE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/${noteID}/link`, + method: 'put', + data: {objects}, + successMessage: "Successfully linked the note" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function unlinkNote(noteID, objects) { + /** + objects: [ + { + "id": "5951303fc9bf3c7b9a573a3f", + "type": "Group" + }, + ] + */ + return { + types: ['UNLINK_NOTE_FRONTEND','UNLINK_NOTE_SUCCESS_BACKEND','UNLINK_NOTE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/${noteID}/unlink`, + method: 'put', + data: {objects}, + successMessage: "Successfully unlinked the note" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + +export function createNoteType(noteType) { + /** + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Ink" + } + */ + return { + types: ['CREATE_NOTETYPE_FRONTEND','CREATE_NOTETYPE_SUCCESS_BACKEND','CREATE_NOTETYPE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/type`, + method: 'post', + data: {noteType}, + successMessage: "Successfully created the note type" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + +export function updateNoteType(noteType) { + /** + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Ink", + "old_type": "Inkss" + } + */ + return { + types: ['UPDATE_NOTETYPE_FRONTEND','UPDATE_NOTETYPE_SUCCESS_BACKEND','UPDATE_NOTETYPE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/type`, + method: 'put', + data: {noteType}, + successMessage: "Successfully updated the note type" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + + +export function deleteNoteType(noteType) { + /** + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Ink" + } + */ + return { + types: ['DELETE_NOTETYPE_FRONTEND','DELETE_NOTETYPE_SUCCESS_BACKEND','DELETE_NOTETYPE_FAILED_BACKEND'], + payload: { + request : { + url: `/notes/type`, + method: 'delete', + data: {noteType}, + successMessage: "Successfully deleted the note type" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/projectActions.js b/viscoll-app/src/actions/backend/projectActions.js new file mode 100644 index 00000000..031b4d84 --- /dev/null +++ b/viscoll-app/src/actions/backend/projectActions.js @@ -0,0 +1,168 @@ +export function loadProject(projectID, showLoading='SHOW_LOADING') { + return { + types: [showLoading,'LOAD_PROJECT_SUCCESS','LOAD_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'get', + successMessage: "" , + errorMessage: "Ooops! Something went wrong", + }, + } + }; +} + +export function loadProjectViewOnly(projectID, showLoading = 'SHOW_LOADING') { + return { + types: [showLoading, 'LOAD_PROJECT_VIEW_ONLY_SUCCESS', 'LOAD_PROJECT_VIEW_ONLY_FAILED'], + payload: { + request: { + url: `/projects/${projectID}/viewOnly`, + method: 'get', + successMessage: "", + errorMessage: "Ooops! Something went wrong", + }, + } + }; +} + + +export function createProject(newProject) { + return { + types: ['SHOW_LOADING','CREATE_PROJECT_SUCCESS','CREATE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects`, + method: 'post', + data: newProject, + successMessage: "Successfully created the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateProject(projectID, project) { + return { + types: ['UPDATE_PROJECT_FRONTEND','UPDATE_PROJECT_SUCCESS_BACKEND','UPDATE_PROJECT_FAILED_BACKEND'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'put', + data: {project}, + successMessage: "Successfully updated the project", + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updatePreferences(projectID, project) { + return { + types: ['UPDATE_PREFERENCES_FRONTEND','','UPDATE_PROJECT_FAILED_BACKEND'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'put', + data: {project}, + successMessage: "Successfully updated the project", + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteProject(projectID, deleteUnlinkedImages) { + return { + types: ['DELETE_PROJECT_FRONTEND','DELETE_PROJECT_SUCCESS_BACKEND','DELETE_PROJECT_FAILED_BACKEND'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'delete', + data: {deleteUnlinkedImages}, + successMessage: "Successfully deleted the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function loadProjects() { + return { + types: ['NO_LOADING','LOAD_PROJECTS_SUCCESS','LOAD_PROJECTS_FAILED'], + payload: { + request : { + url: `/projects`, + method: 'get', + successMessage: "" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +/** + * + * @param {object} data {importData:"", importFormat:"", imageData:""} + */ +export function importProject(data) { + return { + types: ['SHOW_LOADING','IMPORT_PROJECT_SUCCESS','IMPORT_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/import`, + method: 'put', + data: data, + successMessage: "Successfully imported the project", + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function cloneProject(projectID) { + return { + types: ['SHOW_LOADING','CLONE_PROJECT_SUCCESS','CLONE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/clone`, + method: 'get', + successMessage: "Successfully cloned the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function exportProject(projectID, format) { + return { + types: ['SHOW_LOADING','EXPORT_SUCCESS','EXPORT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/export/${format}`, + method: 'get', + successMessage: "" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function exportProjectBeforeFeedback(projectID, format) { + return { + types: ['NO_LOADING','EXPORT_SUCCESS','EXPORT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/export/${format}`, + method: 'get', + successMessage: "You have successfully sent a feedback!" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/sideActions.js b/viscoll-app/src/actions/backend/sideActions.js new file mode 100644 index 00000000..134015b7 --- /dev/null +++ b/viscoll-app/src/actions/backend/sideActions.js @@ -0,0 +1,31 @@ +export function updateSide(sideID, side) { + return { + types: ['UPDATE_SIDE_FRONTEND','UPDATE_SIDE_SUCCESS_BACKEND','UPDATE_SIDE_FAILED_BACKEND'], + payload: { + request : { + url: `/sides/${sideID}`, + method: 'put', + data: {side}, + successMessage: "Successfully updated the side" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} + +export function updateSides(sides) { + return { + types: ['UPDATE_SIDES_FRONTEND','UPDATE_SIDES_SUCCESS_BACKEND','UPDATE_SIDES_FAILED_BACKEND'], + payload: { + request : { + url: `/sides`, + method: 'put', + data: {sides}, + successMessage: "Successfully updated the sides" , + errorMessage: "Ooops! Something went wrong" + } + }, + isUndoable: true, + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/userActions.js b/viscoll-app/src/actions/backend/userActions.js new file mode 100644 index 00000000..10edcd03 --- /dev/null +++ b/viscoll-app/src/actions/backend/userActions.js @@ -0,0 +1,153 @@ + +export function login(session) { + return { + types: ['NO_LOADING','LOGIN_SUCCESS','LOGIN_FAILED'], + payload: { + request : { + url: `/session`, + method: 'post', + data: { session }, + successMessage: "You have successfully logged in" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function register(user) { + return { + types: ['NO_LOADING','REGISTER_SUCCESS','REGISTER_FAILED'], + payload: { + request : { + url: `/registration`, + method: 'post', + data: {user}, + successMessage: "You have successfully registered" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function confirm(confirmation_token) { + return { + types: ['NO_LOADING','CONFIRM_SUCCESS','CONFIRM_FAILED'], + payload: { + request : { + url: `/confirmation`, + method: 'put', + data: { confirmation_token }, + successMessage: "You have successfully confirmed your account" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function resendConfirmation(email) { + return { + type: 'RESEND_CONFIRMATION', + payload: { + request : { + url: `/confirmation`, + method: 'post', + data: { + confirmation: { + email: email + } + }, + successMessage: "You have successfully resent the confirmation email" , + errorMessage: "Ooops! Something went wrong" + } + } + } +} + +export function logout() { + return { + types: ['NO_LOADING','LOGOUT_SUCCESS','LOGOUT_FAILED'], + payload: { + request : { + url: `/session`, + method: 'delete', + successMessage: "You have successfully logged out" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function resetPasswordRequest(email) { + return { + types: ['NO_LOADING','REQUEST_RESET_SUCCESS','REQUEST_RESET_FAILED'], + payload: { + request : { + url: `/password`, + method: 'post', + data: {password: { email }}, + successMessage: "You have successfully requested to reset password" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function resetPassword(reset_password_token, password) { + return { + types: ['NO_LOADING','RESET_SUCCESS','RESET_FAILED'], + payload: { + request : { + url: `/password`, + method: 'put', + data: {reset_password_token, password}, + successMessage: "You have successfully reset your password" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateProfile(user, userID) { + return { + types: ['SHOW_LOADING','UPDATE_PROFILE_SUCCESS','UPDATE_PROFILE_FAILED'], + payload: { + request : { + url: `/users/${userID}`, + method: 'put', + data: user, + successMessage: "You have successfully updated your account" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteProfile(userID) { + return { + types: ['SHOW_LOADING','DELETE_PROFILE_SUCCESS','DELETE_PROFILE_FAILED'], + payload: { + request : { + url: `/users/${userID}`, + method: 'delete', + successMessage: "You have successfully deleted your account" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function sendFeedback(title, message, browserInformation, project) { + return { + types: ['NO_LOADING', 'SEND_FEEDBACK_SUCCESS', 'SEND_FEEDBACK_FAILED'], + payload: { + request: { + url: `/feedback`, + method: 'post', + data: {title, message, browserInformation, project}, + successMessage: "You have successfully sent a feedback!", + errorMessage: "Ooops! Something went wrong" + } + } + } +} + diff --git a/viscoll-app/src/actions/frontend/after/imageActions.js b/viscoll-app/src/actions/frontend/after/imageActions.js new file mode 100644 index 00000000..d460e568 --- /dev/null +++ b/viscoll-app/src/actions/frontend/after/imageActions.js @@ -0,0 +1,14 @@ +export function updateImagesAfterUpload(action, dashboard, active) { + const newDIYImages = action.payload.images + dashboard.images = [...dashboard.images, ...newDIYImages] + if (active.project.id !== ""){ + // Update the active project's DIYManifest images list + active.project.manifests.DIYImages.images = [ + ...active.project.manifests.DIYImages.images, + ...newDIYImages.map(image => { + return { label: image.label, url: image.url, manifestID: "DIYImages" } + }) + ] + } + return {dashboard, active} +} diff --git a/viscoll-app/src/actions/frontend/before/groupActions.js b/viscoll-app/src/actions/frontend/before/groupActions.js new file mode 100644 index 00000000..4b7690e0 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/groupActions.js @@ -0,0 +1,126 @@ +import { createLeaves, deleteLeaf } from './leafActions'; +import { getLeafMembers } from './helperActions'; + +export function createGroups(action, state) { + const parentGroupID = action.payload.request.data.additional.parentGroupID + const parentGroup = parentGroupID ? state.project.Groups[parentGroupID] : null + const noOfGroups = action.payload.request.data.additional.noOfGroups + const noOfLeaves = action.payload.request.data.additional.noOfLeafs + const memberOrder = action.payload.request.data.additional.memberOrder + const globalOrder = action.payload.request.data.additional.order + const autoConjoin = action.payload.request.data.additional.conjoin + const oddMemberLeftOut = action.payload.request.data.additional.oddMemberLeftOut + const groupIDs = action.payload.request.data.additional.groupIDs + const leafIDs = action.payload.request.data.additional.leafIDs + const sideIDs = action.payload.request.data.additional.sideIDs + const title = action.payload.request.data.group.title + const type = action.payload.request.data.group.type + const direction = action.payload.request.data.group.direction + const tacketed = action.payload.request.data.group.tacketed + const sewing = action.payload.request.data.group.sewing + let newlyAddedGroupIDs = [] + for (let count = 0; count < noOfGroups; count++) { + // Create new Group with give groupID + state.project.Groups["Group_" + groupIDs[count]] = { + id: "Group_" + groupIDs[count], + title: title? title : "None", + type: type? type : "Quire", + direction: direction, + tacketed: tacketed? tacketed : [], + sewing: sewing? sewing : [], + nestLevel: parentGroup ? parentGroup.nestLevel+1 : 1, + parentID: parentGroup ? parentGroup.id : null, + notes: [], + memberType: "Group", + memberIDs: leafIDs.slice(count*noOfLeaves, count*noOfLeaves+noOfLeaves).map(leafID => "Leaf_"+leafID) + } + newlyAddedGroupIDs.push("Group_" + groupIDs[count]) + } + // Add newlyAddedGroupIDs to the parentGroup's memberIDs list if parentGroup exist + if (parentGroup) state.project.Groups[parentGroupID].memberIDs.splice(memberOrder-1, 0, ...newlyAddedGroupIDs) + // Add newlyAddedGroupIDs to the active project's groupIDs list + state.project.groupIDs.splice(globalOrder-1, 0, ...newlyAddedGroupIDs) + // Populate leafIDs recursively and replace the active project's leafIDs list + let updatedLeafIDs = []; + for (let groupID of state.project.groupIDs) { + const group = state.project.Groups[groupID]; + if (group.nestLevel===1) getLeafMembers(group.memberIDs, state, updatedLeafIDs) + } + state.project.leafIDs = updatedLeafIDs; + // Create new leaves for each new Group + let groupCount = 0 + for (let groupID of newlyAddedGroupIDs){ + const action = {payload: {request: {data: { + leaf: { parentID: groupID }, + additional: { + noOfLeafs: noOfLeaves, + conjoin: autoConjoin, + oddMemberLeftOut: oddMemberLeftOut, + leafIDs: leafIDs.slice(groupCount*noOfLeaves, groupCount*noOfLeaves+noOfLeaves), + sideIDs: sideIDs.slice(groupCount*2*noOfLeaves, groupCount*2*noOfLeaves+2*noOfLeaves) + } + }}}} + state = createLeaves(action, state, true) + groupCount += 1 + } + // Generate the list of new Groups and Leaves to flash + state.collationManager.flashItems.leaves = leafIDs.map((leafID)=>"Leaf_"+leafID); + state.collationManager.flashItems.groups = [...newlyAddedGroupIDs] + return state +} + +export function updateGroup(action, state) { + const updatedGroupID = action.payload.request.url.split("/").pop(); + const updatedGroup = action.payload.request.data.group + // Update the group with id updatedGroupID + state.project.Groups[updatedGroupID] = { ...state.project.Groups[updatedGroupID], ...updatedGroup } + return state +} + +export function updateGroups(action, state) { + const updatedGroups = action.payload.request.data.groups + for (let updatedGroup of updatedGroups) { + // Update the group of id updatedGroup.id with attributes updatedGroup.attributes + state.project.Groups[updatedGroup.id] = { ...state.project.Groups[updatedGroup.id], ...updatedGroup.attributes } + } + return state +} + +export function deleteGroup(deletedGroupID, state) { + const deletedGroup = state.project.Groups[deletedGroupID] + // Remove deletedGroupID from groupIDs list + let deletedGroupIDIndex = state.project.groupIDs.indexOf(deletedGroupID) + state.project.groupIDs.splice(deletedGroupIDIndex, 1) + // Unlink all Notes of deletedGroupID + for (let noteID in state.project.Notes) { + deletedGroupIDIndex = state.project.Notes[noteID].objects.Group.indexOf(deletedGroupID) + if (deletedGroupIDIndex !== -1) + state.project.Notes[noteID].objects.Group.splice(deletedGroupIDIndex, 1) + } + // Remove deletedGroupID from deletedGroupParent's memberIDs list if exists + if (deletedGroup.parentID ) { + const deletedGroupParent = state.project.Groups[deletedGroup.parentID] + deletedGroupIDIndex = deletedGroupParent.memberIDs.indexOf(deletedGroupID) + state.project.Groups[deletedGroup.parentID].memberIDs.splice(deletedGroupIDIndex, 1) + } + // Remove all Group members of deletedGroup + for (let memberID of [...deletedGroup.memberIDs]){ + if (memberID.charAt(0)==="G") + deleteGroup(memberID, state) + else + deleteLeaf(memberID, state) + } + // Reset selectedObjects to empty list + state.collationManager.selectedObjects = { type: "", members: [], lastSelected: "" } + // Remove the deletedGroupID from Groups + delete state.project.Groups[deletedGroupID] + return state +} + +export function deleteGroups(deletedGroupIDs, state) { + for (let deletedGroupID of deletedGroupIDs) { + if (state.project.Groups.hasOwnProperty(deletedGroupID)) + deleteGroup(deletedGroupID, state) + } + return state +} diff --git a/viscoll-app/src/actions/frontend/before/helperActions.js b/viscoll-app/src/actions/frontend/before/helperActions.js new file mode 100644 index 00000000..73512b56 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/helperActions.js @@ -0,0 +1,9 @@ +export function getLeafMembers(memberIDs, state, leafIDs=[]) { + for (let memberID of memberIDs){ + if (memberID.charAt(0)==="G"){ + getLeafMembers(state.project.Groups[memberID].memberIDs, state, leafIDs) + } else { + leafIDs.push(memberID) + } + } +} \ No newline at end of file diff --git a/viscoll-app/src/actions/frontend/before/imageActions.js b/viscoll-app/src/actions/frontend/before/imageActions.js new file mode 100644 index 00000000..886f8b88 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/imageActions.js @@ -0,0 +1,93 @@ +export function linkImages(action, dashboard, active) { + if (active.project.id!=="") + linkImagesFromProject(action, dashboard, active) + linkImagesFromDashboard(action, dashboard) + return {dashboard, active} +} + +export function unlinkImages(action, dashboard, active) { + if (active.project.id !== "") + unlinkImagesFromProject(action, dashboard, active) + unlinkImagesFromDashboard(action, dashboard) + return { dashboard, active } +} + +export function deleteImages(action, dashboard, active) { + if (active.project.id !== "") + unlinkImagesFromProject(action, dashboard, active) + deleteImagesFromDashboard(action, dashboard) + return { dashboard, active } +} + +export function linkImagesFromProject(action, dashboard, active) { + const imageIDs = action.payload.request.data.imageIDs + for (let imageID of imageIDs) { + // Add image of imageID to the list of DIYImages in active project + const imageIndex = dashboard.images.findIndex(image => image.id === imageID) + const image = dashboard.images[imageIndex] + active.project.manifests.DIYImages.images.push({ + label: image.label, + url: image.url, + manifestID: "DIYImages" + }) + } +} + +function findByLabel (DIYImage) { + return DIYImage.label === this.label; +} + +export function unlinkImagesFromProject(action, dashboard, active) { + const imageIDs = action.payload.request.data.imageIDs; + for (let imageID of imageIDs) { + // Remove image of imageID from the list of DIYImages in active project + let imageIndex = dashboard.images.findIndex(image => image.id === imageID) + const image = dashboard.images[imageIndex] + imageIndex = active.project.manifests.DIYImages.images.findIndex(findByLabel, {label:image.label}) + active.project.manifests.DIYImages.images.splice(imageIndex, 1) + // Unlink all sides of this project if it was mapped to this image + for (let sideID of image.sideIDs) { + if (active.project.Rectos.hasOwnProperty(sideID)) + active.project.Rectos[sideID].image = {} + if (active.project.Versos.hasOwnProperty(sideID)) + active.project.Versos[sideID].image = {} + } + } +} + +export function linkImagesFromDashboard(action, dashboard) { + const projectIDs = action.payload.request.data.projectIDs + const imageIDs = action.payload.request.data.imageIDs + for (let projectID of projectIDs){ + // Add projectID to the list of projectIDs for each image of imageIDs + for (let imageID of imageIDs) { + let imageIndex = dashboard.images.findIndex(image => image.id === imageID) + let projectIDIndex = dashboard.images[imageIndex].projectIDs.indexOf(projectID) + if (projectIDIndex === -1) + dashboard.images[imageIndex].projectIDs.push(projectID) + } + } +} + +export function unlinkImagesFromDashboard(action, dashboard) { + const projectIDs = action.payload.request.data.projectIDs + const imageIDs = action.payload.request.data.imageIDs + for (let projectID of projectIDs) { + // Remove projectID from the list of projectIDs for each image of imageIDs + for (let imageID of imageIDs) { + let imageIndex = dashboard.images.findIndex(image => image.id === imageID) + let projectIDIndex = dashboard.images[imageIndex].projectIDs.indexOf(projectID) + if (projectIDIndex !== -1) + dashboard.images[imageIndex].projectIDs.splice(projectIDIndex, 1) + } + } +} + +export function deleteImagesFromDashboard(action, dashboard) { + const imageIDs = action.payload.request.data.imageIDs + // Remove imageID from dashboard.images for each image of imageIDs + for (let imageID of imageIDs) { + let imageIndex = dashboard.images.findIndex(image => image.id === imageID) + dashboard.images.splice(imageIndex, 1) + } +} \ No newline at end of file diff --git a/viscoll-app/src/actions/frontend/before/leafActions.js b/viscoll-app/src/actions/frontend/before/leafActions.js new file mode 100644 index 00000000..a1c0eca3 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/leafActions.js @@ -0,0 +1,264 @@ +import { getLeafMembers } from './helperActions'; + +export function autoConjoinLeafs(action, state, leaves, oddMemberLeftOut=false) { + // Remove the existing conjoined_to of each leaf if it is already conjoined_to another leaf + for (let leafID of leaves){ + const leafConjoinedToID = state.project.Leafs[leafID].conjoined_to + if (leafConjoinedToID) { + state.project.Leafs[leafConjoinedToID].conjoined_to = null + } + } + // Remove the oddLeafID from leaves list + if (leaves.length%2===1){ + let oddLeafID; + if (oddMemberLeftOut) + oddLeafID = leaves[oddMemberLeftOut-1] + else + oddLeafID = leaves[Math.floor(leaves.length/2)] + const oddLeafIDIndex = leaves.indexOf(oddLeafID) + leaves.splice(oddLeafIDIndex, 1) + state.project.Leafs[oddLeafID].conjoined_to = null + } + for (let i = 0; i < leaves.length; i++) { + if (leaves.length / 2 === i) + break + else { + const leafOneID = leaves[i] + const leafTwoID = leaves[leaves.length - i - 1] + state.project.Leafs[leafOneID].conjoined_to = leafTwoID + state.project.Leafs[leafTwoID].conjoined_to = leafOneID + } + } + return state +} + +export function createLeaves(action, state, fromGroupCreation=false) { + const parentGroupID = action.payload.request.data.leaf.parentID + const parentGroup = state.project.Groups[parentGroupID] + const noOfLeaves = action.payload.request.data.additional.noOfLeafs + const memberOrder = action.payload.request.data.additional.memberOrder + const globalOrder = action.payload.request.data.additional.order + const autoConjoin = action.payload.request.data.additional.conjoin + const oddMemberLeftOut = action.payload.request.data.additional.oddMemberLeftOut + const leafIDs = action.payload.request.data.additional.leafIDs + const sideIDs = action.payload.request.data.additional.sideIDs + let newlyAddedLeafIDs = [] + let sideCount = 0 + for (let count = 0; count < noOfLeaves; count++) { + // Create new Leaf with give leafID + state.project.Leafs["Leaf_" + leafIDs[count]] = { + id: "Leaf_" + leafIDs[count], + material: action.payload.request.data.leaf.material? action.payload.request.data.leaf.material : "None", + type: action.payload.request.data.leaf.type? action.payload.request.data.leaf.type : "None", + conjoined_to: null, + attached_above: "None", + attached_below: "None", + stub: action.payload.request.data.leaf.stub? action.payload.request.data.leaf.stub : "None", + nestLevel: parentGroup.nestLevel, + parentID: parentGroupID, + rectoID: "Recto_" + sideIDs[sideCount], + versoID: "Verso_" + sideIDs[sideCount+1], + notes: [], + memberType: "Leaf" + } + newlyAddedLeafIDs.push("Leaf_" + leafIDs[count]) + // Create new Recto with given rectoID + state.project.Rectos["Recto_" + sideIDs[sideCount]] = { + id: "Recto_" + sideIDs[sideCount], + parentID: "Leaf_" + leafIDs[count], + folio_number: null, + texture: "Hair", + image: {}, + script_direction: "None", + notes: [], + memberType: "Recto" + } + state.project.rectoIDs.push("Recto_" + sideIDs[sideCount]); + // Create new Verso with given rectoID + state.project.Versos["Verso_" + sideIDs[sideCount+1]] = { + id: "Verso_" + sideIDs[sideCount+1], + parentID: "Leaf_" + leafIDs[count], + folio_number: null, + texture: "Flesh", + image: {}, + script_direction: "None", + notes: [], + memberType: "Verso" + } + state.project.versoIDs.push("Verso_" + sideIDs[sideCount+1]); + sideCount += 2 + } + if (!fromGroupCreation) { + // Add newlyAddedLeafIDs to the parentGroup's memberIDs list + state.project.Groups[parentGroupID].memberIDs.splice(memberOrder-1, 0, ...newlyAddedLeafIDs) + // Add newlyAddedLeafIDs to the active project's leafIDs list + if (globalOrder) + state.project.leafIDs.splice(globalOrder-1, 0, ...newlyAddedLeafIDs) + else { + // Populate leafIDs recursively and replace the active project's leafIDs list + let updatedLeafIDs = []; + for (let groupID of state.project.groupIDs) { + const group = state.project.Groups[groupID]; + if (group.nestLevel===1) getLeafMembers(group.memberIDs, state, updatedLeafIDs) + } + state.project.leafIDs = updatedLeafIDs; + } + // AutoConjoin newlyAddedLeaves if necessary + } + if (autoConjoin) autoConjoinLeafs(action, state, newlyAddedLeafIDs, oddMemberLeftOut) + // Generate the list of new Leaves to flash + state.collationManager.flashItems.leaves = [...newlyAddedLeafIDs] + return state +} + +export function updateLeaf(action, state) { + const updatedLeafID = action.payload.request.url.split("/").pop(); + const updatedLeaf = action.payload.request.data.leaf; + // Do side effects if necessary + if (action.payload.request.data.leaf.hasOwnProperty("conjoined_to")) { + state = handleConjoin(state, updatedLeafID, action.payload.request.data.leaf.conjoined_to); + } else if (action.payload.request.data.leaf.hasOwnProperty("attached_above")) { + state = handleAttachAbove(state, updatedLeafID, action.payload.request.data.leaf.attached_above); + } else if (action.payload.request.data.leaf.hasOwnProperty("attached_below")) { + state = handleAttachBelow(state, updatedLeafID, action.payload.request.data.leaf.attached_below); + } else if (action.payload.request.data.leaf.hasOwnProperty("material") && action.payload.request.data.leaf.material==="Paper") { + state = handlePaperUpdate(state, updatedLeafID); + } + // Update the leaf with id updatedLeafID + state.project.Leafs[updatedLeafID] = { ...state.project.Leafs[updatedLeafID], ...updatedLeaf } + return state +} + +function handleConjoin(state, leafID, conjoinedToID) { + const leaf = state.project.Leafs[leafID]; + if (leaf.conjoined_to) { + state.project.Leafs[leaf.conjoined_to].conjoined_to = null; + } + if (conjoinedToID) { + const conjoinedToLeaf = state.project.Leafs[conjoinedToID]; + if (conjoinedToLeaf.conjoined_to) { + state.project.Leafs[conjoinedToLeaf.conjoined_to].conjoined_to = null; + } + state.project.Leafs[conjoinedToID].conjoined_to = leafID; + } + return state; +} + +function handleAttachAbove(state, leafID, attachType) { + const aboveLeafID = state.project.leafIDs[state.project.leafIDs.indexOf(leafID) - 1]; + state.project.Leafs[aboveLeafID].attached_below = attachType; + return state; +} + +function handleAttachBelow(state, leafID, attachType) { + const belowLeafID = state.project.leafIDs[state.project.leafIDs.indexOf(leafID) + 1]; + state.project.Leafs[belowLeafID].attached_above = attachType; + return state; +} + +function handlePaperUpdate(state, leafID) { + const leaf = state.project.Leafs[leafID]; + state.project.Rectos[leaf.rectoID].texture = "None"; + state.project.Versos[leaf.versoID].texture = "None"; + return state; +} + +export function updateLeaves(action, state) { + const updatedLeaves = action.payload.request.data.leafs + for (let updatedLeaf of updatedLeaves) { + // Update the leaf of id updatedLeaf.id with attributes updatedLeaf.attributes + state.project.Leafs[updatedLeaf.id] = { ...state.project.Leafs[updatedLeaf.id], ...updatedLeaf.attributes }; + if (updatedLeaf.attributes.hasOwnProperty("material") && updatedLeaf.attributes.material==="Paper") { + state = handlePaperUpdate(state, updatedLeaf.id); + } + } + return state +} + +export function deleteLeaf(deletedLeafID, state) { + const deletedLeaf = state.project.Leafs[deletedLeafID] + const deletedLeafParent = state.project.Groups[deletedLeaf.parentID] + const deletedLeafMemberIndex = deletedLeafParent.memberIDs.indexOf(deletedLeafID) + // Detach deletedLeaf's conjoined leaf if exists + if (deletedLeaf.conjoined_to !== null) + state.project.Leafs[deletedLeaf.conjoined_to].conjoined_to = null + // Detach deletedLeaf's attached_above leaf if exists + if (deletedLeaf.attached_above !== "None"){ + const attachedAboveLeafID = deletedLeafParent.memberIDs[deletedLeafMemberIndex-1] + if (attachedAboveLeafID){ // deletedLeaf could be the first leaf in Group + state.project.Leafs[attachedAboveLeafID].attached_below = "None" + } + } + // Detach deletedLeaf's attached_below leaf if exists + if (deletedLeaf.attached_below !== "None") { + const attachedBelowLeafID = deletedLeafParent.memberIDs[deletedLeafMemberIndex+1] + if (attachedBelowLeafID) // deletedLeaf could be the last leaf in Group + state.project.Leafs[attachedBelowLeafID].attached_above = "None" + } + // Remove deletedLeafID from leafIDs list + let deletedLeafIDIndex = state.project.leafIDs.indexOf(deletedLeafID) + state.project.leafIDs.splice(deletedLeafIDIndex, 1) + // Remove deletedLeafID from deletedLeafParent's memberIDs list + deletedLeafIDIndex = deletedLeafParent.memberIDs.indexOf(deletedLeafID) + state.project.Groups[deletedLeaf.parentID].memberIDs.splice(deletedLeafIDIndex, 1) + // Update deletedLeafParent's tacketed if deletedLeafID is present + deletedLeafIDIndex = deletedLeafParent.tacketed.indexOf(deletedLeafID) + if (deletedLeafIDIndex !== -1) + state.project.Groups[deletedLeaf.parentID].tacketed.splice(deletedLeafIDIndex, 1) + // Update deletedLeafParent's sewing if deletedLeafID is present + deletedLeafIDIndex = deletedLeafParent.sewing.indexOf(deletedLeafID) + if (deletedLeafIDIndex !== -1) + state.project.Groups[deletedLeaf.parentID].sewing.splice(deletedLeafIDIndex, 1) + // Unlink all Notes of deletedLeafID. Also unlink Notes in Recto and Verso of deletedLeafID + for (let noteID in state.project.Notes) { + deletedLeafIDIndex = state.project.Notes[noteID].objects.Leaf.indexOf(deletedLeafID) + let rectoIDIndex = state.project.Notes[noteID].objects.Recto.indexOf(deletedLeaf.rectoID) + let versoIDIndex = state.project.Notes[noteID].objects.Verso.indexOf(deletedLeaf.versoID) + if (deletedLeafIDIndex !== -1) + state.project.Notes[noteID].objects.Leaf.splice(deletedLeafIDIndex, 1) + if (rectoIDIndex !== -1) + state.project.Notes[noteID].objects.Recto.splice(rectoIDIndex, 1) + if (versoIDIndex !== -1) + state.project.Notes[noteID].objects.Verso.splice(versoIDIndex, 1) + } + // Remove deletedLeaf's Recto and Verso IDs from Rectos, rectoIDs, Versos, versoIDs + delete state.project.Rectos[deletedLeaf.rectoID] + delete state.project.Versos[deletedLeaf.versoID] + const rectoIDIndex = state.project.rectoIDs.indexOf(deletedLeaf.rectoID) + state.project.rectoIDs.splice(rectoIDIndex, 1) + const versoIDIndex = state.project.versoIDs.indexOf(deletedLeaf.versoID) + state.project.versoIDs.splice(versoIDIndex, 1) + // Reset selectedObjects to empty list + state.collationManager.selectedObjects = {type: "", members: [], lastSelected: ""} + // Remove the deletedLeafID from Leafs + delete state.project.Leafs[deletedLeafID] + return state +} + +export function deleteLeaves(deletedLeafIDs, state) { + for (let deletedLeafID of deletedLeafIDs) { + deleteLeaf(deletedLeafID, state) + } + return state +} + +export function generateFolioPageNumbers(action, state, folioOrPage) { + let numberCount = action.payload.request.data.startNumber; + let rectoIDs = action.payload.request.data.rectoIDs; + let versoIDs = action.payload.request.data.versoIDs; + for (const index in rectoIDs) { + const recto = state.project.Rectos[rectoIDs[index]]; + const verso = state.project.Versos[versoIDs[index]]; + if (folioOrPage==="folio_number") { + recto[folioOrPage] = numberCount + recto.id[0]; + verso[folioOrPage] = numberCount + verso.id[0]; + + } else { + recto[folioOrPage] = numberCount; + numberCount++; + verso[folioOrPage] = numberCount; + } + numberCount++; + } + return state +} \ No newline at end of file diff --git a/viscoll-app/src/actions/frontend/before/manifestActions.js b/viscoll-app/src/actions/frontend/before/manifestActions.js new file mode 100644 index 00000000..dfe061dd --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/manifestActions.js @@ -0,0 +1,24 @@ +export function updateManifest(action, state) { + const updatedManifest = action.payload.request.data.manifest + // Only manifest name is allowed to be updated + state.project.manifests[updatedManifest.id].name = updatedManifest.name + return state +} + +export function deleteManifest(action, state) { + const deletedManifest = action.payload.request.data.manifest + // Delete the manifest with id deletedManifest.id from the active project's manifests + delete state.project.manifests[deletedManifest.id] + // Update all sides that have an image mapped from deletedManifest + for (let rectoID of [...Object.keys(state.project.Rectos)]){ + const rectoSide = state.project.Rectos[rectoID] + if (rectoSide.image.hasOwnProperty('manifestID') && rectoSide.image.manifestID===deletedManifest.id) + state.project.Rectos[rectoID].image = {} + } + for (let versoID of [...Object.keys(state.project.Versos)]) { + const versoSide = state.project.Versos[versoID] + if (versoSide.image.hasOwnProperty('manifestID') && versoSide.image.manifestID === deletedManifest.id) + state.project.Versos[versoID].image = {} + } + return state +} diff --git a/viscoll-app/src/actions/frontend/before/noteActions.js b/viscoll-app/src/actions/frontend/before/noteActions.js new file mode 100644 index 00000000..ca481c47 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/noteActions.js @@ -0,0 +1,109 @@ +export function createNoteType(action, state) { + const newNoteType = action.payload.request.data.noteType.type + state.project.noteTypes.push(newNoteType) + return state +} + +export function updateNoteType(action, state) { + const updatedNoteType = action.payload.request.data.noteType.type + const oldNoteType = action.payload.request.data.noteType.old_type + // Rename the noteType of each Note that had oldNoteType + for (let noteID in state.project.Notes){ + if (state.project.Notes[noteID].type === oldNoteType) + state.project.Notes[noteID].type = updatedNoteType + } + // Rename the noteType in the noteTypes array + const oldNoteTypeIndex = state.project.noteTypes.indexOf(oldNoteType) + state.project.noteTypes[oldNoteTypeIndex] = updatedNoteType + return state +} + +export function deleteNoteType(action, state) { + const deletedNoteType = action.payload.request.data.noteType.type + // Rename the noteType of each Note that had deleteNoteType to Unknown + for (let noteID in state.project.Notes) { + if (state.project.Notes[noteID].type === deletedNoteType) + state.project.Notes[noteID].type = "Unknown" + } + // Delete the noteType from the noteTypes array + const deletedNoteTypeIndex = state.project.noteTypes.indexOf(deletedNoteType) + state.project.noteTypes.splice(deletedNoteTypeIndex, 1) + return state +} + + +export function createNote(action, state) { + const newNote = action.payload.request.data.note + // Add new note to Notes + state.project.Notes[newNote.id] = { + id: newNote.id, + title: newNote.title, + type: newNote.type, + description: newNote.description, + show: newNote.show, + objects: { Group: [], Leaf: [], Recto: [], Verso: [] } + } + return state +} + +export function updateNote(action, state) { + const updatedNoteID = action.payload.request.url.split("/").pop(); + const updatedNote = action.payload.request.data.note + // Update the note with id updatedNoteID + state.project.Notes[updatedNoteID] = { ...state.project.Notes[updatedNoteID], ...updatedNote } + return state +} + +export function linkNote(action, state) { + const linkedNoteID = action.payload.request.url.split("/").slice(-2)[0]; + const linkedObjects = action.payload.request.data.objects + // Update each object with linkedNoteID + for (let object of linkedObjects){ + if (object.type==="Side") + object.type = object.id.charAt(0) === "R" ? "Recto" : "Verso" + state.project[`${object.type}s`][object.id].notes.push(linkedNoteID) + // Update the objects property of note with linkedNoteID + state.project.Notes[linkedNoteID].objects[object.type].push(object.id) + } + return state +} + +export function unlinkNote(action, state) { + const unlinkedNoteID = action.payload.request.url.split("/").slice(-2)[0]; + const unlinkedObjects = action.payload.request.data.objects + // Update each object by removing unlinkedNoteID + for (let object of unlinkedObjects) { + if (object.type === "Side") + object.type = object.id.charAt(0) === "R" ? "Recto" : "Verso" + const unlinkedNoteIDIndex = state.project[`${object.type}s`][object.id].notes.indexOf(unlinkedNoteID) + state.project[`${object.type}s`][object.id].notes.splice(unlinkedNoteIDIndex, 1) + // Update the objects property of note with unlinkedNoteID + const unlinkedObjectIDIndex = state.project.Notes[unlinkedNoteID].objects[object.type].indexOf(object.id) + state.project.Notes[unlinkedNoteID].objects[object.type].splice(unlinkedObjectIDIndex, 1) + } + return state +} + +export function deleteNote(action, state) { + const deletedNoteID = action.payload.request.url.split("/").pop(); + // Delete the reference on all Groups,Leaves,Sides that this deletedNote had + for (let groupID of state.project.Notes[deletedNoteID].objects.Group) { + const deletedNoteIDIndex = state.project.Groups[groupID].notes.indexOf(deletedNoteID) + state.project.Groups[groupID].notes.splice(deletedNoteIDIndex, 1) + } + for (let leafID of state.project.Notes[deletedNoteID].objects.Leaf) { + const deletedNoteIDIndex = state.project.Leafs[leafID].notes.indexOf(deletedNoteID) + state.project.Leafs[leafID].notes.splice(deletedNoteIDIndex, 1) + } + for (let rectoID of state.project.Notes[deletedNoteID].objects.Recto) { + const deletedNoteIDIndex = state.project.Rectos[rectoID].notes.indexOf(deletedNoteID) + state.project.Rectos[rectoID].notes.splice(deletedNoteIDIndex, 1) + } + for (let versoID of state.project.Notes[deletedNoteID].objects.Verso) { + const deletedNoteIDIndex = state.project.Versos[versoID].notes.indexOf(deletedNoteID) + state.project.Versos[versoID].notes.splice(deletedNoteIDIndex, 1) + } + // Delete the note with id deletedNoteID in Notes + delete state.project.Notes[deletedNoteID] + return state +} \ No newline at end of file diff --git a/viscoll-app/src/actions/frontend/before/projectActions.js b/viscoll-app/src/actions/frontend/before/projectActions.js new file mode 100644 index 00000000..e0c7cd93 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/projectActions.js @@ -0,0 +1,36 @@ +export function updateProject(action, state){ + const updatedProject = action.payload.request.data.project; + const updatedProjectID = action.payload.request.url.split("/").pop(); + const projectIndex = state.projects.findIndex(project => project.id === updatedProjectID) + state.projects[projectIndex] = {...state.projects[projectIndex], ...updatedProject}; + return state +} + +export function updatePreferences(action, state) { + const preferences = action.payload.request.data.project.preferences; + state.project.preferences = { + group:{...state.project.preferences.group, ...preferences.group}, + leaf:{...state.project.preferences.leaf, ...preferences.leaf}, + side:{...state.project.preferences.side,...preferences.side} + }; + return state; +} + +export function deleteProject(action, state) { + const deletedProjectID = action.payload.request.url.split("/").pop(); + const deletedProjectIndex = state.projects.findIndex(project => project.id === deletedProjectID); + state.projects.splice(deletedProjectIndex, 1) + // Remove deletedProjectID from all images. If image has no projects linked, delete the image. + for (let image of [...state.images]){ + const projectIDIndex = image.projectIDs.indexOf(deletedProjectID) + const imageIndex = state.images.findIndex(DIYImage => DIYImage.id === image.id) + if (projectIDIndex !== -1) { + state.images[imageIndex].projectIDs.splice(projectIDIndex, 1) + } + // Remove the image if its not linked to any other projects + if (projectIDIndex!==-1 && image.projectIDs.length===0 && action.payload.request.data.deleteUnlinkedImages) { + state.images.splice(imageIndex, 1) + } + } + return state +} diff --git a/viscoll-app/src/actions/frontend/before/sideActions.js b/viscoll-app/src/actions/frontend/before/sideActions.js new file mode 100644 index 00000000..17e2a7e6 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/sideActions.js @@ -0,0 +1,71 @@ +export function updateSide(action, state) { + const updatedSideID = action.payload.request.url.split("/").pop(); + const updatedSide = action.payload.request.data.side + // Update the side with id updatedSideID + if (updatedSideID.charAt(0)==="R") + state.project.Rectos[updatedSideID] = { ...state.project.Rectos[updatedSideID], ...updatedSide } + else + state.project.Versos[updatedSideID] = { ...state.project.Versos[updatedSideID], ...updatedSide } + return state +} + +export function updateSides(action, state) { + const updatedSides = action.payload.request.data.sides + for (let updatedSide of updatedSides) { + // Update the side of id updatedSide.id with attributes updatedSide.attributes + if (updatedSide.id.charAt(0) === "R") + state.project.Rectos[updatedSide.id] = { ...state.project.Rectos[updatedSide.id], ...updatedSide.attributes } + else + state.project.Versos[updatedSide.id] = { ...state.project.Versos[updatedSide.id], ...updatedSide.attributes } + } + return state +} + +export function mapSides(action, active, dashboard) { + // SPEICAL CASE FOR DIY IMAGE MAPPING + const mappedSides = action.payload.request.data.sides + for (let mappedSide of mappedSides){ + const mappedSideID = mappedSide.id + const mappedSideImage = mappedSide.attributes.image + const sideNameKey = mappedSideID.charAt(0) === "R" ? "Rectos" : "Versos" + const currentSideImage = active.project[sideNameKey][mappedSideID].image + let imageLinkedID = false + // If an Image was linked, check if it is a DIY Image and link mappedSideID to the Image + if (mappedSideImage.hasOwnProperty('manifestID') && mappedSideImage.manifestID==='DIYImages'){ + imageLinkedID = mappedSideImage.url.split("/").pop().split("_")[0] + let imageLinkedIDIndex = dashboard.images.findIndex(image => image.id===imageLinkedID) + if (imageLinkedIDIndex>=0) { + let mappedSideIDIndex = dashboard.images[imageLinkedIDIndex].sideIDs.indexOf(mappedSideID) + // Link mappedSideID to this image + if (mappedSideIDIndex===-1) + dashboard.images[imageLinkedIDIndex].sideIDs.push(mappedSideID) + } + } + // Check if this mappedSideID is now already linked to another DIY Image and unlink this mappedSideID from that Image + if (mappedSideImage.hasOwnProperty('manifestID') && currentSideImage.hasOwnProperty('manifestID') && currentSideImage.manifestID === 'DIYImages') { + let imageUnlinkedID = currentSideImage.url.split("/").pop().split("_")[0] + if (!imageLinkedID || imageLinkedID !== imageUnlinkedID) { + let imageUnlinkedIDIndex = dashboard.images.findIndex(image => image.id === imageUnlinkedID) + let mappedSideIDIndex = dashboard.images[imageUnlinkedIDIndex].sideIDs.indexOf(mappedSideID) + if (mappedSideIDIndex !== -1) + dashboard.images[imageUnlinkedIDIndex].sideIDs.splice(mappedSideIDIndex, 1) + } + } + // If an Image was unlinked, check if it was a DIY Image and unlink mappedSideID from the Image + if (!mappedSideImage.hasOwnProperty('manifestID') && currentSideImage.hasOwnProperty('manifestID') && currentSideImage.manifestID==='DIYImages'){ + let imageID = currentSideImage.url.split("/").pop().split("_")[0] + let imageIndex = dashboard.images.findIndex(image => image.id === imageID) + if (imageIndex>=0) { + let mappedSideIDIndex = dashboard.images[imageIndex].sideIDs.indexOf(mappedSideID) + if (mappedSideIDIndex !== -1) + dashboard.images[imageIndex].sideIDs.splice(mappedSideIDIndex, 1) + } + } + } + updateSides(action, active) // this will handle updating the 'image' field of all mapped Sides + return { dashboard, active } +} + + + + diff --git a/viscoll-app/src/actions/undoRedo/groupHelper.js b/viscoll-app/src/actions/undoRedo/groupHelper.js new file mode 100644 index 00000000..4985a6d6 --- /dev/null +++ b/viscoll-app/src/actions/undoRedo/groupHelper.js @@ -0,0 +1,133 @@ +import { + addGroups, + updateGroup, + updateGroups, + deleteGroups, +} from '../backend/groupActions'; + +import { + helperUndoDeleteLeaves +} from './leafHelper'; + +import { + linkNote, +} from '../backend/noteActions'; + +export function undoCreateGroups(action, state) { + const groupIDs = action.payload.request.data.additional.groupIDs.map((id)=>{return ("Group_"+id)}); + const deleteRequest = deleteGroups({groups: groupIDs}, state.project.id); + return [deleteRequest]; +} + +export function undoUpdateGroups(action, state) { + const requestData = action.payload.request.data.groups; + let groups = []; + for (const request of requestData) { + const group = state.project.Groups[request.id]; + let item = { + id: request.id, + attributes: {}, + } + for (let attribute in request.attributes) { + if (!request.attributes.hasOwnProperty(attribute)) continue; + item.attributes[attribute] = group[attribute]; + } + groups.push(item) + } + const historyAction = updateGroups(groups); + return [historyAction]; +} + +export function undoUpdateGroup(action, state) { + const groupID = action.payload.request.url.split("/").pop(); + let attribute = Object.keys(action.payload.request.data.group)[0]; + + let group = { + [attribute]: state.project.Groups[groupID][attribute], + } + const historyAction = updateGroup(groupID, group); + return [historyAction]; +} + +export function undoDeleteGroup(action, state) { + const groupID = action.payload.request.url.split("/").pop(); + const historyActions = helperUndoDeleteGroup(groupID, state); + return historyActions; +} + +export function undoDeleteGroups(action, state) { + const groupIDs = action.payload.request.data.groups; + let historyActions = []; + for (const groupID of groupIDs) { + const group = state.project.Groups[groupID]; + if (!(group.parentID && groupIDs.includes(group.parentID))) { + historyActions = historyActions.concat(helperUndoDeleteGroup(groupID, state)); + } + } + return historyActions; +} + +function helperUndoDeleteGroup(groupID, state) { + const group = state.project.Groups[groupID]; + let historyActions = []; + // Create emtpy group + const groupInfo = { + project_id: state.project.id, + title: group.title, + type: group.type, + tacketed: group.tacketed, + sewing: group.sewing, + } + const additional = { + noOfGroups: 1, + leafIDs: [], + sideIDs: [], + groupIDs: [groupID.split("_")[1]], + order: state.project.groupIDs.indexOf(groupID) + 1, + } + if (group.parentID) { + additional["memberOrder"] = state.project.Groups[group.parentID].memberIDs.indexOf(groupID) + 1; + additional["parentGroupID"] = group.parentID; + } else { + additional["memberOrder"] = additional.order; + } + historyActions.push(addGroups(groupInfo, additional)); + + // Populate members + const groupedMembers = helperSeparateMembers(group.memberIDs); + for (let members of groupedMembers) { + if (members[0][0]==="L") { + historyActions = historyActions.concat(helperUndoDeleteLeaves(members, state)); + } else { + // Recurse! + historyActions = historyActions.concat(helperUndoDeleteGroup(members[0], state)); + } + } + // Link notes to group + if (group.notes.length>0) { + const objects = [{ id: groupID, type: "Group" }]; + for (const noteID of group.notes) { + const noteRequest = linkNote(noteID, objects); + historyActions.push(noteRequest); + } + } + return historyActions; +} + +/** + * Separate members into groups of leaves and groups + */ +function helperSeparateMembers(memberIDs) { + let result = memberIDs.length>0? [[memberIDs[0]]] : []; + for (let i=1; i{return ("Leaf_"+id)}); + const deleteRequest = deleteLeafs({leafs: leafIDs}); + return [deleteRequest]; +} + +export function undoUpdateLeaf(action, state) { + const historyActions = []; + const leafID = action.payload.request.url.split("/").pop(); + const attribute = Object.keys(action.payload.request.data.leaf)[0]; + + const leaf = { + [attribute]: state.project.Leafs[leafID][attribute], + } + historyActions.push(updateLeaf(leafID, leaf)); + if (attribute==="material" && (action.payload.request.data.leaf.material==="Paper" || action.payload.request.data.leaf.material==="Parchment")) { + historyActions.push(updateSides([ + { + id: state.project.Leafs[leafID].rectoID, + attributes: {texture: state.project.Rectos[state.project.Leafs[leafID].rectoID].texture}, + }, + { + id: state.project.Leafs[leafID].versoID, + attributes: {texture: state.project.Versos[state.project.Leafs[leafID].versoID].texture}, + } + ])) + } + return historyActions; +} + +export function undoUpdateLeaves(action, state) { + const requestData = action.payload.request.data.leafs; + const leafs = []; + const historyActions = []; + + for (const request of requestData) { + const leaf = state.project.Leafs[request.id]; + const item = { + id: request.id, + attributes: {}, + } + for (const attribute in request.attributes) { + if (!request.attributes.hasOwnProperty(attribute)) continue; + item.attributes[attribute] = leaf[attribute]; + if (attribute==="material" && (request.attributes[attribute]==="Paper"||request.attributes[attribute]==="Parchment")) { + historyActions.push(updateSides([ + { + id: state.project.Leafs[leaf.id].rectoID, + attributes: {texture: state.project.Rectos[state.project.Leafs[leaf.id].rectoID].texture}, + }, + { + id: state.project.Leafs[leaf.id].versoID, + attributes: {texture: state.project.Versos[state.project.Leafs[leaf.id].versoID].texture}, + } + ])) + } + } + leafs.push(item) + } + historyActions.push(updateLeafs(leafs, state.project.id)); + return historyActions; +} + +export function undoDeleteLeaves(action, state) { + const historyActions = helperUndoDeleteLeaves(action.payload.request.data.leafs, state); + return historyActions; +} + +export function undoDeleteLeaf(action, state) { + const leafID = action.payload.request.url.split("/").pop(); + const historyActions = helperUndoDeleteLeaves([leafID], state); + return historyActions; +} + +/** + * Params + * leafIDs list of leaf IDs that may not belong to the same parent nor are sequential + * state active tree state + * Returns [ [leafID, leafID,..], ... ] + */ +function helperSeparateLeavesByGroup(leafIDs, state) { + const leafNeighbours = [[leafIDs[0]]]; + for (let i=1; i[state.project.Leafs[leafID].rectoID.split("_")[1], state.project.Leafs[leafID].versoID.split("_")[1]])).reduce((a,b)=>a.concat(b)); + + const leaf = { + project_id: state.project.id, + parentID: parentID, + nestLevel: state.project.Groups[parentID].nestLevel, + } + const additional = { + conjoin: false, + leafIDs: leafIDs.map((id)=>id.split("_")[1]), + memberOrder: state.project.Groups[parentID].memberIDs.indexOf(leafIDs[0]) + 1, + noOfLeafs: leafIDs.length, + order: state.project.leafIDs.indexOf(leafIDs[0]) + 1, + sideIDs: sideIDs, + } + const createRequest = addLeafs(leaf, additional); + historyActions.push(createRequest); + } + + // Update leaves attributes + const leafs = []; + for (const leafID of leafIDs) { + const leaf = state.project.Leafs[leafID]; + let attributes = {}; + attributes['type'] = leaf.type; + attributes['material'] = leaf.material; + attributes['stub'] = leaf.stub; + if (leaf.conjoined_to) { + attributes['conjoined_to'] = leaf.conjoined_to; + // Update the conjoin partner leaf too + leafs.push({id:leaf.conjoined_to, attributes:{conjoined_to:leafID}}); + } + if (leaf.attached_above!=="None") { + attributes['attached_above'] = leaf.attached_above; + // Update the above leaf too + const leafAboveID = state.project.leafIDs[state.project.leafIDs.indexOf(leafID)-1]; + if (!leafIDs.includes(leafAboveID)) leafs.push({id:leafAboveID, attributes:{attached_below:leaf.attached_above}}); + } + if (leaf.attached_below!=="None") { + attributes['attached_below'] = leaf.attached_below; + // Update the below leaf too + const leafBelowID = state.project.leafIDs[state.project.leafIDs.indexOf(leafID)+1]; + if (!leafIDs.includes(leafBelowID))leafs.push({id:leafBelowID, attributes:{attached_above:leaf.attached_below}}); + } + leafs.push({ + id: leafID, + attributes, + }); + } + const updateLeafsRequest = updateLeafs(leafs, state.project.id); + historyActions.push(updateLeafsRequest); + + // Update side attributes + const sides = []; + for (const leafID of leafIDs) { + const leaf = state.project.Leafs[leafID]; + const recto = state.project.Rectos[leaf.rectoID]; + const verso = state.project.Versos[leaf.versoID]; + sides.push({ + id: recto.id, + attributes: { + texture: recto.texture, + folio_number: recto.folio_number? recto.folio_number : "", + script_direction: recto.script_direction, + } + }) + sides.push({ + id: verso.id, + attributes: { + texture: verso.texture, + folio_number: verso.folio_number? verso.folio_number : "", + script_direction: verso.script_direction, + } + }); + } + const sideRequest = updateSides(sides); + historyActions.push(sideRequest); + + // Link notes + for (const leafID of leafIDs) { + const leaf = state.project.Leafs[leafID]; + const recto = state.project.Rectos[leaf.rectoID]; + const verso = state.project.Versos[leaf.versoID]; + + if (leaf.notes.length>0) { + const objects = [{ id: leafID, type: "Leaf" }]; + for (const noteID of leaf.notes) { + const noteRequest = linkNote(noteID, objects); + historyActions.push(noteRequest); + } + } + if (recto.notes.length>0) { + const objects = [{ id: recto.id, type: "Side" }]; + for (const noteID of recto.notes) { + const noteRequest = linkNote(noteID, objects); + historyActions.push(noteRequest); + } + } + if (verso.notes.length>0) { + const objects = [{ id: verso.id, type: "Side" }]; + for (const noteID of verso.notes) { + const noteRequest = linkNote(noteID, objects); + historyActions.push(noteRequest); + } + } + } + // Update parent group if leaves were part of sewing/tacket + const groupsRequest = []; + for (const leafID of leafIDs) { + const leaf = state.project.Leafs[leafID]; + const group = state.project.Groups[leaf.parentID]; + if (group.sewing.length>0 && (group.sewing[0]===leafID || (group.sewing[1] && group.sewing[1]===leafID))) { + groupsRequest.push({ + id: group.id, + attributes: { + sewing: group.sewing, + } + }); + } + if (group.tacketed.length>0 && (group.tacketed[0]===leafID || (group.tacketed[1] && group.tacketed[1]===leafID))) { + groupsRequest.push({ + id: group.id, + attributes: { + tacketed: group.tacketed, + } + }); + } + } + if (groupsRequest.length>0) historyActions.push(updateGroups(groupsRequest)) + return historyActions; +} + +export function undoAutoconjoin(action, state) { + const leafIDs = action.payload.request.data.leafs; + const leafs = []; + for (const leafID of leafIDs) { + leafs.push({ + id: leafID, + attributes: {conjoined_to: state.project.Leafs[leafID].conjoined_to} + }) + } + const historyAction = updateLeafs(leafs, state.project.id); + return [historyAction]; +} + +export function undoFolioPageNumbers(action, state, folioOrPage) { + const sideIDs = action.payload.request.data.rectoIDs.concat(action.payload.request.data.versoIDs); + const sides = []; + for (const sideID of sideIDs) { + const sideType = sideID.split("_")[0] + "s"; + const item = { + id: sideID, + attributes: { + [folioOrPage]: state.project[sideType][sideID][folioOrPage], + } + } + sides.push(item); + } + const historyActions = updateSides(sides); + return [historyActions]; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/undoRedo/manifestHelper.js b/viscoll-app/src/actions/undoRedo/manifestHelper.js new file mode 100644 index 00000000..10a7b462 --- /dev/null +++ b/viscoll-app/src/actions/undoRedo/manifestHelper.js @@ -0,0 +1,61 @@ +import { + createManifest, + updateManifest, +} from '../backend/manifestActions'; + +import { + updateSides, +} from '../backend/sideActions'; + +export function undoUpdateManifest(action, state) { + const manifest = { + id: action.payload.request.data.manifest.id, + name: state.project.manifests[action.payload.request.data.manifest.id].name, + } + const historyAction = updateManifest(state.project.id, {manifest}); + return [historyAction]; +} + +export function undoDeleteManifest(action, state) { + const historyActions = []; + const manifestID = action.payload.request.data.manifest.id; + // Create manifest + const manifest = { + id: manifestID, + url: state.project.manifests[manifestID].url, + } + const createAction = createManifest(state.project.id, {manifest}); + historyActions.push(createAction); + + // Relink sides linked to images in this manifest + const sides = []; + for (const rectoID of state.project.rectoIDs) { + const recto = state.project.Rectos[rectoID]; + if (recto.image.manifestID && recto.image.manifestID === manifestID) { + const item = { + id: recto.id, + attributes: { + image: {...recto.image}, + } + } + sides.push(item); + } + } + for (const versoID of state.project.versoIDs) { + const verso = state.project.Versos[versoID]; + if (verso.image.manifestID && verso.image.manifestID === manifestID) { + const item = { + id: verso.id, + attributes: { + image: {...verso.image}, + } + }; + sides.push(item); + } + } + if (sides.length>0) { + const mapAction = updateSides(sides); + historyActions.push(mapAction); + } + return historyActions; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/undoRedo/noteHelper.js b/viscoll-app/src/actions/undoRedo/noteHelper.js new file mode 100644 index 00000000..2441fb97 --- /dev/null +++ b/viscoll-app/src/actions/undoRedo/noteHelper.js @@ -0,0 +1,96 @@ +import { + deleteNoteType, + updateNoteType, + createNoteType, + updateNote, + unlinkNote, + linkNote, + addNote, +} from '../backend/noteActions'; + +export function undoUpdateNoteType(action, state) { + const noteType = { + project_id: state.project.id, + old_type: action.payload.request.data.noteType.type, + type: action.payload.request.data.noteType.old_type, + } + const historyAction = updateNoteType(noteType); + return [historyAction]; +} + +export function undoCreateNoteType(action, state) { + const noteType = { + project_id: state.project.id, + type: action.payload.request.data.noteType.type, + } + const historyAction = deleteNoteType(noteType); + return [historyAction]; +} + +export function undoDeleteNoteType(action, state) { + const historyActions = []; + // Create note type + const type = action.payload.request.data.noteType.type; + const noteType = { + project_id: state.project.id, + type, + } + historyActions.push(createNoteType(noteType)); + // Update notes that had this note type + for (const key in state.project.Notes) { + if (!state.project.Notes.hasOwnProperty(key)) continue; + if (state.project.Notes[key].type === type) { + historyActions.push(updateNote(key, {type})); + } + } + return historyActions; +} + +export function undoLinkNote(action, state) { + const urlSplit = action.payload.request.url.split("/"); + const noteID = urlSplit[urlSplit.length-2]; + const historyAction = unlinkNote(noteID, action.payload.request.data.objects); + return [historyAction]; +} + +export function undoUnlinkNote(action, state) { + const urlSplit = action.payload.request.url.split("/"); + const noteID = urlSplit[urlSplit.length-2]; + const historyAction = linkNote(noteID, action.payload.request.data.objects); + return [historyAction]; +} + +export function undoDeleteNote(action, state) { + const historyActions = []; + const noteID = action.payload.request.url.split("/").pop(); + const note = state.project.Notes[noteID]; + + // Create note + const noteData = { + project_id: state.project.id, + id: noteID, + title: note.title, + type: note.type, + description: note.description, + show: note.show, + } + historyActions.push(addNote(noteData)); + + // Relink leaves, groups, sides + const objects = []; + for (const id of note.objects.Group) { + objects.push({id, type:"Group"}); + } + for (const id of note.objects.Leaf) { + objects.push({id, type:"Leaf"}); + } + for (const id of note.objects.Recto) { + objects.push({id, type:"Recto"}); + } + for (const id of note.objects.Verso) { + objects.push({id, type:"Verso"}); + } + historyActions.push(linkNote(noteID, objects)); + + return historyActions; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/undoRedo/sideHelper.js b/viscoll-app/src/actions/undoRedo/sideHelper.js new file mode 100644 index 00000000..b836bf15 --- /dev/null +++ b/viscoll-app/src/actions/undoRedo/sideHelper.js @@ -0,0 +1,36 @@ +import { + updateSide, + updateSides, +} from '../backend/sideActions'; + +export function undoUpdateSide(action, state) { + const sideID = action.payload.request.url.split("/").pop(); + const attribute = Object.keys(action.payload.request.data.side)[0]; + const sideType = sideID.split("_")[0] + "s"; + const side = { + [attribute]: state.project[sideType][sideID][attribute], + }; + const historyAction = updateSide(sideID, side); + return [historyAction]; +} + +export function undoUpdateSides(action, state) { + const requests = action.payload.request.data.sides; + const sides = []; + + for (const request of requests) { + const sideType = request.id.split("_")[0] + "s"; + const side = state.project[sideType][request.id]; + const item = { + id: request.id, + attributes: {}, + } + for (const attribute in request.attributes) { + if (!request.attributes.hasOwnProperty(attribute)) continue; + item.attributes[attribute] = side[attribute]; + } + sides.push(item); + } + const historyActions = updateSides(sides); + return [historyActions]; +} \ No newline at end of file diff --git a/viscoll-app/src/assets/blank_page.png b/viscoll-app/src/assets/blank_page.png new file mode 100644 index 00000000..6958b3a6 Binary files /dev/null and b/viscoll-app/src/assets/blank_page.png differ diff --git a/viscoll-app/src/assets/collation.png b/viscoll-app/src/assets/collation.png new file mode 100644 index 00000000..706fcfc6 Binary files /dev/null and b/viscoll-app/src/assets/collation.png differ diff --git a/viscoll-app/src/assets/logo_white.png b/viscoll-app/src/assets/logo_white.png new file mode 100644 index 00000000..6b756189 Binary files /dev/null and b/viscoll-app/src/assets/logo_white.png differ diff --git a/viscoll-app/src/assets/logo_white.svg b/viscoll-app/src/assets/logo_white.svg new file mode 100644 index 00000000..be04257f --- /dev/null +++ b/viscoll-app/src/assets/logo_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/viscoll-app/src/assets/viscoll_loading.gif b/viscoll-app/src/assets/viscoll_loading.gif new file mode 100644 index 00000000..13d6923b Binary files /dev/null and b/viscoll-app/src/assets/viscoll_loading.gif differ diff --git a/viscoll-app/src/assets/visualMode/PaperGroup.js b/viscoll-app/src/assets/visualMode/PaperGroup.js new file mode 100644 index 00000000..b306c475 --- /dev/null +++ b/viscoll-app/src/assets/visualMode/PaperGroup.js @@ -0,0 +1,129 @@ +import paper from 'paper'; + +PaperGroup.prototype = { + constructor: PaperGroup, + draw: function() { + // Create rectangle shapes + const rightIndent = (this.direction === "left-to-right") ? 0 : (this.spacing*(this.nestLevel-1)); + let rectangle = new paper.Rectangle( + new paper.Point(this.x+10, this.y), + new paper.Size(this.width-this.x-rightIndent-20, this.groupHeight) + ); + if (this.viewingMode) { + rectangle = new paper.Rectangle( + new paper.Point(this.x+10, this.y), + new paper.Size(this.width-rightIndent-this.x, this.groupHeight) + ); + } + let highlightRectangle = rectangle.clone(); + highlightRectangle.set( + new paper.Point(this.x, this.y-10), + new paper.Size(this.width-rightIndent-this.x, this.groupHeight+20) + ); + + // Create path from rectangle + this.path = new paper.Path.Rectangle(rectangle); + if (this.isActive) { + this.path.fillColor = this.groupColorActive; + } else { + this.path.fillColor = this.groupColor; + } + if (this.group.nestLevel%2===0) { + this.path.fillColor.brightness -= 0.05; + } + this.path.name = "group " + this.groupOrder; + + // Create highlight path from rectangle + this.highlight = new paper.Path.Rectangle(highlightRectangle); + this.highlight.fillColor = new paper.Color(112/255.0, 229/255.0, 220/255.0, 1); + this.highlight.opacity = 0; + this.highlight.name = "group " + this.groupOrder + " highlight"; + this.highlight.insertBelow(this.path); + + this.filterHighlight = new paper.Path.Rectangle(highlightRectangle); + this.filterHighlight.fillColor = this.filterColor; + this.filterHighlight.opacity = 0; + this.filterHighlight.insertBelow(this.path); + + // Set highlight path to be at the back + paper.project.activeLayer.insertChild(0, this.highlight); + paper.project.activeLayer.insertChild(0, this.filterHighlight); + }, + setMouseEventHandlers: function() { + // Set mouse event handlers + let that = this; + this.path.onClick = function(event) { + that.handleObjectClick(that.group, event); + }; + this.path.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.path.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + }, + removeMouseEventHandlers: function() { + this.path.onClick = function(event) {}; + this.path.onMouseEnter = function(event) {}; + this.path.onMouseLeave = function(event) {}; + }, + activate: function() { + this.path.fillColor = this.groupColorActive; + this.isActive = true; + this.highlight.opacity = 0.75; + this.highlight.fillColor = "#ffffff"; + }, + deactivate: function() { + this.path.fillColor = this.groupColor; + if (this.group.nestLevel%2===0) { + this.path.fillColor.brightness -= 0.05; + } + this.isActive = false; + this.highlight.opacity = 0; + this.highlight.fillColor = new paper.Color(112/255.0, 229/255.0, 220/255.0, 1); + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + let groupText = this.group.type + " " + this.groupOrder; + if (this.visibleAttributes && this.visibleAttributes.title) groupText = groupText + ": " + this.group.title; + this.text.set({ + content: groupText, + }); + if(this.direction === "right-to-left"){ + this.text.set({ + point: [this.width-this.text.bounds.width-this.text.point.x-(this.spacing*(this.nestLevel-1)), this.text.point.y], + }) + } + }, +} +// Constructor for group +function PaperGroup(args) { + this.manager = args.manager; + this.group = args.group; + this.groupOrder = args.groupIDs.indexOf(args.group.id)+1 + this.direction = args.direction; + this.y = args.y; + this.x = args.x; + this.nestLevel = args.nestLevel; + this.spacing = args.spacing; + this.width = args.width; + this.groupHeight = args.groupHeight; + this.isActive = args.isActive; + this.highlight = new paper.Path(); + this.filterHighlight = new paper.Path(); + this.path = new paper.Path(); + this.groupColor = args.groupColor; + this.groupColorActive = args.groupColorActive; + this.textColor = args.textColor; + this.filterColor = args.filterColor; + this.handleObjectClick = args.handleObjectClick; + this.visibleAttributes = {}; + this.viewingMode = args.viewingMode; + this.text = new paper.PointText({ + point: [this.x+args.spacing*0.6, this.y+args.spacing*0.6], + fillColor: this.textColor, + fontSize: args.spacing*0.48, + }); + this.setVisibility(args.visibleAttributes); +} +export default PaperGroup; diff --git a/viscoll-app/src/assets/visualMode/PaperLeaf.js b/viscoll-app/src/assets/visualMode/PaperLeaf.js new file mode 100644 index 00000000..bbabbc80 --- /dev/null +++ b/viscoll-app/src/assets/visualMode/PaperLeaf.js @@ -0,0 +1,631 @@ +import paper from 'paper'; +PaperLeaf.prototype = { + constructor: PaperLeaf, + draw: function() { + // Call this function only after ALL leaves have been instantiated + // This is because we need all leaves present in order + // to compute their indentations relative to each other + this.setIndent(); + // Draw horizontal part + let x1, x2 + if(!this.groupDirection || this.groupDirection === "left-to-right"){ + x1 = this.multiplier<1? 10 + this.indent*this.spacing*this.multiplier : this.indent*this.spacing*this.multiplier; + x2 = this.width; + } else { + x1 = this.groupWidth - (this.multiplier < 1 ? 10 + this.indent*this.spacing*this.multiplier : this.indent*this.spacing*this.multiplier); + x2 = this.groupWidth - this.width; + this.oldPointX = x2; + } + this.dirMultiplier = (!this.groupDirection || this.groupDirection === "left-to-right") ? 1 : -1; + + this.path.add(new paper.Point(x1, this.y)); + if (this.leaf.stub !== "None" && (!this.groupDirection || this.groupDirection === "left-to-right")) { + x2 = this.width*0.15+x1; + } else if (this.leaf.stub !== "None") { + x2 = (x1-this.width*0.15)-x2; + } + + this.path.add(new paper.Point(x2, this.y)); + // Draw vertical part + if (this.isConjoined()) { + var conjoinY=this.y_conjoin_center(this.conjoined_to); + this.path.insert(0, new paper.Point(this.path.segments[0].point.x, conjoinY)); + } + this.curveMe(); + + this.path.name = "leaf " + this.order; + + // Create highlight path + this.highlight = this.path.clone(); + this.highlight.dashArray = [0, 0]; + this.highlight.segments[this.highlight.segments.length-1].point.x = this.highlight.segments[this.highlight.segments.length-1].point.x + 5; + if (this.leaf.conjoined_to === "None") { + this.highlight.segments[0].point.x = this.highlight.segments[0].point.x - 5; + } + this.highlight.strokeColor = this.strokeColorActive; + this.highlight.strokeWidth = this.path.strokeWidth*2; + this.highlight.opacity = 0; + this.highlight.name = "leaf " + this.order + " highlight"; + this.highlight.insertBelow(this.path); + + if (this.isActive) { + this.highlight.opacity = 0.35; + this.highlight.strokeWidth = this.path.strokeWidth*2; + this.highlight.strokeColor = "#ffffff"; + } + + this.filterHighlight = this.path.clone(); + this.filterHighlight.dashArray = [0, 0]; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x + 5; + if (this.leaf.conjoined_to === "None") { + this.filterHighlight.segments[0].point.x = this.filterHighlight.segments[0].point.x - 5; + } + this.filterHighlight.strokeColor = this.strokeColorFilter; + this.filterHighlight.strokeWidth = this.path.strokeWidth*2; + this.filterHighlight.opacity = 0; + this.filterHighlight.insertBelow(this.path); + + // this.path.fullySelected = true; + this.showAttributes(); + this.createAttachments(); + + const leafNotesToShow = this.leaf.notes.filter((noteID)=>{return this.Notes[noteID].show}).reverse(); + const rectoNotesToShow = this.recto.notes.filter((noteID)=>{return this.Notes[noteID].show}).reverse(); + const versoNotesToShow = this.verso.notes.filter((noteID)=>{return this.Notes[noteID].show}); + + let textX = 0; + let textY = this.y; + let fontSize = this.spacing*0.50; + let numChars = this.path.bounds.width/fontSize*2.4; + + if (this.isConjoined()) { + // This leaf is conjoined + textX = this.path.segments[1].point.x; + } else { + // Separate leaf + textX = this.path.segments[0].point.x+this.dirMultiplier*10; + } + if (this.leaf.attached_above.includes("Partial")) { + // This leaf has a partial glue attachment + // Place text to the right of attachment drawing + if(!this.groupDirection || this.groupDirection === "left-to-right"){ + textX = this.attachment.bounds.right+5; + } else { + textX = this.attachment.bounds.left - 5; + } + } else if (this.leaf.attached_above.includes("Glued")) { + // Other type of glueing exists + if(!this.groupDirection || this.groupDirection === "left-to-right"){ + textY -= this.spacing*0.7; + } else { + textY += this.spacing*0.7; + } + } + let that = this; + let clickListener = function(note) { + return function(event) { + that.openNoteDialog(note); + } + } + // Draw recto note text + for (let noteIndex = 0; noteIndex < rectoNotesToShow.length; noteIndex++) { + const note = this.Notes[rectoNotesToShow[noteIndex]]; + const noteTitle = this.recto.folio_number? "â–¼ " + this.recto.folio_number + " : " + note.title.substr(0,numChars) : "â–¼ R : " + note.title.substr(0,numChars) ; + let textNote = new paper.PointText({ + content: noteTitle, + point: [textX, textY - noteIndex*(this.spacing*0.7) - this.spacing*0.3], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + const textPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? textX : textX-textNote.bounds.width; + textNote.set({point: [textPointX, textY - noteIndex * (this.spacing * 0.7) - this.spacing * 0.3]}); + textNote.onClick = !this.viewingMode && clickListener(note); + this.textNotes.addChild(textNote); + } + // Draw leaf note text + for (let noteIndex = 0; noteIndex < leafNotesToShow.length; noteIndex++) { + const note = this.Notes[leafNotesToShow[noteIndex]]; + + let textNote = new paper.PointText({ + content: "â–¼ L" + this.order + " : " + note.title.substr(0,numChars), + fillColor: this.strokeColor, + fontSize: fontSize, + }); + const textPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? textX : textX - textNote.bounds.width; + textNote.set({point: [textPointX, textY - rectoNotesToShow.length*(this.spacing*0.7) - noteIndex*(this.spacing*0.7) - this.spacing*0.3]}); + textNote.onClick = !this.viewingMode && clickListener(note); + this.textNotes.addChild(textNote); + } + // Draw verso note text + for (let noteIndex = 0; noteIndex < versoNotesToShow.length; noteIndex++) { + const note = this.Notes[versoNotesToShow[noteIndex]]; + const noteTitle = this.verso.folio_number? "â–² " + this.verso.folio_number + " : " + note.title.substr(0,numChars) : "â–² V : " + note.title.substr(0,numChars); + let textNote = new paper.PointText({ + content: noteTitle, + point: [textX, this.y + noteIndex*(this.spacing*0.7) + this.spacing*0.8], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + const textPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? textX : textX-textNote.bounds.width; + textNote.set({point: [textPointX, this.y + noteIndex*(this.spacing*0.7) + this.spacing*0.8]}) + textNote.onClick = !this.viewingMode && clickListener(note); + this.textNotes.addChild(textNote); + } + this.textNotes.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textNotes.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + return this; + }, + setMouseEventHandlers: function() { + // Set mouse event handlers + let that = this; + this.path.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.textLeafOrder.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.textRecto.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.textVerso.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.path.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.path.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.textLeafOrder.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textLeafOrder.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.textRecto.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textRecto.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.textVerso.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textVerso.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + }, + removeMouseEventHandlers: function() { + // Set mouse event handlers + this.path.onClick = function(event) {}; + this.textLeafOrder.onClick = function(event) {}; + this.textRecto.onClick = function(event) {}; + this.textVerso.onClick = function(event) {}; + this.path.onMouseEnter = function(event) {}; + this.path.onMouseLeave = function(event) {}; + this.textLeafOrder.onMouseEnter = function(event) {}; + this.textLeafOrder.onMouseLeave = function(event) {}; + this.textRecto.onMouseEnter = function(event) {}; + this.textRecto.onMouseLeave = function(event) {}; + this.textVerso.onMouseEnter = function(event) {}; + this.textVerso.onMouseLeave = function(event) {}; + }, + createAttachments: function() { + if (this.order>1 && this.leaf.attached_above.includes("Glued")) { + this.createGlue(); + } else if (this.order>1 && this.leaf.attached_above==="Other") { + this.createOtherAttachment(); + } + if (this.order>1 && this.leaf.conjoin_type==="Sewn") { + this.createSewn(); + } + }, + createGlue: function() { + let x = this.path.segments[0].point.x; + if (this.isConjoined() && this.conjoined_to (this.path.segments[this.path.segments.length-1].point.x-10)) { + let glueLine = new paper.Path(); + glueLine.add(new paper.Point(x, this.y-this.spacing*0.3)); + glueLine.add(new paper.Point(x-10, this.y-this.spacing*0.7)); + glueLine.strokeColor = "#707070"; + glueLine.strokeWidth = 2; + this.attachment.addChild(glueLine); + x -= 5; + } + } else { + // Complete glue + while (x <= (this.path.segments[this.path.segments.length-1].point.x-10)) { + let glueLine = new paper.Path(); + glueLine.add(new paper.Point(x, this.y-this.spacing*0.3)); + glueLine.add(new paper.Point(x+10, this.y-this.spacing*0.7)); + glueLine.strokeColor = "#707070"; + glueLine.strokeWidth = 2; + this.attachment.addChild(glueLine); + x += 5; + } + } + }, + // Sewing of conjoined leaves + createSewn: function() { + if (this.isConjoined() && this.conjoined_to0; + }, + conjoinedLeaf: function() { + return this.manager.getLeaf(this.conjoined_to); + }, + y_conjoin_center: function(conjoin_order) { + var y1 = this.path.segments[1].point.y; + var y2 = (this.manager.getLeaf(conjoin_order)).getY(); + return y1+((y2-y1)/2.0); + }, + y_nonconjoin_center: function() { + var y = this.y + var y_center = this.y_centerQuire(); + if (y===y_center) { + return y_center; + } else if (y= this.manager.numLeaves()) { + return 0; + } else { + return this.manager.getLeaf(this.order+1).y; + } + }, + curveMe: function() { + var path_height = Math.abs(this.path.segments[0].point.y - this.path.segments[1].point.y); + var midpoint = this.path.segments[1].point; + if (this.isConjoined() ) { + var numLeavesInside = Math.abs(this.conjoined_to - this.order); + // Remove the middle point and insert a new one with handles + this.path.removeSegment(1); + // // Calculate new point's location and radius + var new_x = midpoint.x + this.dirMultiplier*20; + var radius = new_x - this.path.segments[0].point.x; + var s1 = new paper.Segment(new paper.Point(new_x, midpoint.y), new paper.Point(-radius,0), null); + this.path.insert(1, s1); + + if (numLeavesInside > 5) { + // Modify first point's handle so that the curve isn't too curvy + var direction = this.path.segments[0].point.y > this.path.segments[1].point.y? -1 : 1; + var oldPoint = this.path.segments[0].point; + this.path.removeSegment(0); + var s0 = new paper.Segment(new paper.Point(oldPoint.x, oldPoint.y), null, new paper.Point(0,direction*(path_height))); + this.path.insert(0, s0); + } + } + }, + setIndent: function() { + this.indent = this.leaf.nestLevel; + if (this.isConjoined() && this.conjoinedLeaf().indent != null) { + // Leaf is conjoined and conjoiner has indent, so copy that conjoiner's indent + this.indent = this.conjoinedLeaf().indent; + } else if (this.isBelowAConjoined()) { + this.indent = (this.prevPaperLeaf().indent+1); + } else if (this.order>1 && this.parentOrder === this.prevPaperLeaf().parentOrder){ + // Leaf is a sibling of previous leaf, so copy sibling's indent + this.indent = this.prevPaperLeaf().indent; + } + }, + isBelowAConjoined: function() { + return (this.order>1 && this.prevPaperLeaf().isConjoined() && this.prevPaperLeaf().conjoined_to > this.order); + }, + y_centerQuire: function () { + var last_leaf = this.manager.getLastLeaf().getY(); + return (last_leaf+ this.spacing)/2.0 ; + }, + getY: function() { + return this.y; + }, + activate: function() { + this.path.strokeColor = this.strokeColorActive; + this.highlight.opacity = 0.35; + this.highlight.strokeWidth = this.path.strokeWidth*2; + this.highlight.strokeColor = "#ffffff"; + }, + deactivate: function() { + this.highlight.opacity = 0; + this.highlight.strokeWidth = this.path.strokeWidth; + this.highlight.strokeColor = this.strokeColorActive; + + if (this.leaf.type==="Added") { + this.path.strokeColor = this.strokeColorAdded; + } else if (this.leaf.type==="Endleaf") { + this.path.strokeColor = "#727272"; + } else { + this.path.strokeColor = this.strokeColor; + } + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + this.showAttributes(); + }, + showAttributes: function() { + const rectoValues = []; + const versoValues = []; + if (this.visibleAttributes.side) { + if (this.visibleAttributes.side.folio_number) { + if (this.recto.folio_number) rectoValues.push(this.recto.folio_number) + if (this.verso.folio_number) versoValues.push(this.verso.folio_number) + } + if (this.visibleAttributes.side.page_number) { + if (this.recto.page_number) rectoValues.push(this.recto.page_number) + if (this.verso.page_number) versoValues.push(this.verso.page_number) + } + if (this.visibleAttributes.side.texture) { + rectoValues.push(this.recto.texture) + versoValues.push(this.verso.texture) + } + } + let rectoContent = ""; + let versoContent = ""; + for (let i=0; i { + if (this.visibleAttributes.side[key]) return acc+1; + return acc; + } + const visibleAttributeCount = this.visibleAttributes.side? Object.keys(this.visibleAttributes.side).reduce(reducer,0) : 0; + + if (visibleAttributeCount===3) { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*3 : this.oldPointX + this.spacing * 3; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2.8 : this.oldPointX + this.spacing * 2.8; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2.8 : this.oldPointX + this.spacing * 2.8; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2.8 : this.oldPointX - this.textLeafOrder.bounds.width + this.spacing * 2.8; + + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit attribute text + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + this.textRecto.set({content: rectoContent}); + this.textVerso.set({content: versoContent}); + + const textRectoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.4 : this.textLeafOrder.bounds.left - this.textRecto.bounds.width - this.spacing * 0.4; + const textVersoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.4 :this.textLeafOrder.bounds.left - this.textVerso.bounds.width - this.spacing * 0.4; + + this.textRecto.set({ point: [textRectoX, this.textRecto.point.y], }); + this.textVerso.set({ point: [textVersoX, this.textVerso.point.y], }); + } else if (visibleAttributeCount===2) { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2 : this.oldPointX + this.spacing * 2; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*1.8 : this.oldPointX + this.spacing * 1.8; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*1.8 : this.oldPointX + this.spacing * 1.8; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*1.8 : this.oldPointX- this.textLeafOrder.bounds.width + this.spacing * 1.8; + + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit attribute text + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + this.textRecto.set({content: rectoContent}); + this.textVerso.set({content: versoContent}); + + const textRectoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textRecto.bounds.width - this.spacing * 0.2; + const textVersoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textVerso.bounds.width - this.spacing * 0.2; + + this.textRecto.set({ point: [textRectoX, this.textRecto.point.y], }); + this.textVerso.set({ point: [textVersoX, this.textVerso.point.y], }); + } + else if (visibleAttributeCount===1) { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing : this.oldPointX + this.spacing; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*0.8 : this.oldPointX + this.spacing * 0.8; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*0.8 : this.oldPointX + this.spacing * 0.8; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*0.8 : this.oldPointX- this.textLeafOrder.bounds.width + this.spacing * 0.8; + + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit folio number text + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + this.textRecto.set({content: rectoContent}); + this.textVerso.set({content: versoContent}); + + const textRectoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textRecto.bounds.width - this.spacing * 0.2; + const textVersoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textVerso.bounds.width - this.spacing * 0.2; + + this.textRecto.set({ point: [textRectoX, this.textRecto.point.y], }); + this.textVerso.set({ point: [textVersoX, this.textVerso.point.y], }); + } else { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width : this.oldPointX; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width + 5 : this.oldPointX - 5; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width + 5 : this.oldPointX - 5; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width+10 : this.oldPointX - this.textLeafOrder.bounds.width - 5; + + // Reset leaf + if (this.leaf.stub === "None") { + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + } + }, +} +// Constructor for leaf +function PaperLeaf(args) { + this.manager = args.manager; + this.Notes = args.Notes; + this.leafIDs = args.leafIDs; + this.leaf = args.leaf; + this.recto = args.recto; + this.verso = args.verso; + this.order = args.leafIDs.indexOf(args.leaf.id) + 1; + this.parentOrder = args.groupIDs.indexOf(this.leaf.parentID)+1; + this.conjoined_to = this.leaf.conjoined_to===null ? "None" : this.leafIDs.indexOf(this.leaf.conjoined_to)+1; + this.indent = null; + this.origin = args.origin; + this.viewingMode = args.viewingMode; + this.width = this.viewingMode? args.width*0.88 : args.width*0.92; + this.groupDirection = args.Groups[this.leaf.parentID].direction; + this.groupWidth = args.width; + this.spacing = args.spacing; + this.y = args.y; + this.strokeWidth = args.strokeWidth; + this.strokeColor = args.strokeColor; + this.strokeColorActive = args.strokeColorActive; + this.strokeColorGroupActive = args.strokeColorGroupActive; + this.strokeColorFilter = args.strokeColorFilter; + this.strokeColorAdded = args.strokeColorAdded; + this.highlight = new paper.Path(); + this.filterHighlight = new paper.Path(); + this.path = new paper.Path(); + this.isActive = args.isActive; + this.handleObjectClick = args.handleObjectClick; + this.multiplier = args.multiplier; + this.attachment = new paper.Group(); + this.textNotes = new paper.Group(); + this.openNoteDialog = args.openNoteDialog; + + this.textLeafOrder = new paper.PointText({ + point: [this.width, this.y+5], + content: "L"+ this.order, + fillColor: this.strokeColor, + fontSize: this.spacing*0.48, + }); + this.textRecto = new paper.PointText({ + point: [this.width+this.spacing, this.y-3], + fillColor: this.strokeColor, + fontSize: this.spacing*0.37, + }); + this.textVerso = new paper.PointText({ + point: [this.width+this.spacing, this.y+this.spacing*0.37-3], + fillColor: this.strokeColor, + fontSize: this.spacing*0.37, + }); + + this.visibleAttributes = args.visibleAttributes; + // Set path properties + if (this.leaf.type==="Added") { + this.path.strokeColor = args.strokeColorAdded; + } else if (this.leaf.type==="Endleaf") { + this.path.strokeColor = "#919191"; + } else { + this.path.strokeColor = args.strokeColor; + } + if (this.leaf.type==='Missing') { + // If leaf is missing, make stroke dashed + this.path.dashArray = [20, 10]; + } + if (this.leaf.type==='Replaced') { + this.path.strokeColor = "#9d6464"; + } + if (this.isActive) { + this.path.strokeColor = args.strokeColorActive; + } + this.path.strokeWidth = this.strokeWidth; + if (this.leaf.material === "Parchment") { + this.path.strokeWidth = this.path.strokeWidth*1.20; + } else if (this.leaf.material === "Paper") { + this.path.strokeWidth = this.path.strokeWidth*0.80; + } +} + + +export default PaperLeaf; diff --git a/viscoll-app/src/assets/visualMode/PaperManager.js b/viscoll-app/src/assets/visualMode/PaperManager.js new file mode 100644 index 00000000..decb812d --- /dev/null +++ b/viscoll-app/src/assets/visualMode/PaperManager.js @@ -0,0 +1,714 @@ +import paper from 'paper'; +import PaperLeaf from "./PaperLeaf.js"; +import PaperGroup from "./PaperGroup.js"; +import { getMemberOrder } from '../../helpers/getMemberOrder'; + +PaperManager.prototype = { + constructor: PaperManager, + createGroup: function(group) { + let g = new PaperGroup({ + manager: this, + group: group, + groupIDs: this.groupIDs, + direction: group.direction, + y: this.groupYs[this.groupIDs.indexOf(group.id)], + x: (group.direction === "left-to-right") ? (group.nestLevel-1)*(this.spacing) : 0, + width: this.width, + groupHeight: this.getGroupHeight(group), + isActive: this.activeGroups.includes(group.id), + groupColor: this.groupColor, + groupColorActive: this.groupColorActive, + filterColor: this.strokeColorFilter, + handleObjectClick: this.handleObjectClick, + textColor: this.groupTextColor, + visibleAttributes: this.visibleAttributes.group, + viewingMode: this.viewingMode, + spacing: this.spacing, + nestLevel: group.nestLevel, + }); + g.draw(); + g.setMouseEventHandlers(); + + // Add this group to collections + this.groupGroups.addChild(g.filterHighlight); + this.groupGroups.addChild(g.highlight); + this.groupGroups.addChild(g.path); + this.groupGroups.addChild(g.text); + this.paperGroups.push(g); + + // Add this group to list of items to flash if it's in the flashItems list + if (this.flashItems.groups.includes(group.id)) { + this.flashGroups.push(g); + } + + }, + createLeaf: function(leaf) { + let l = new PaperLeaf({ + manager: this, + origin: this.origin, + width: this.width, + spacing: this.spacing, + strokeWidth: this.strokeWidth * Math.min(1.0, this.multipliers[this.leafIDs.indexOf(leaf.id)+1]), + strokeColor: this.strokeColor, + strokeColorActive: this.strokeColorActive, + strokeColorGroupActive: this.strokeColorGroupActive, + strokeColorAdded: this.strokeColorAdded, + leaf: leaf, + recto: this.Rectos[leaf.rectoID], + verso: this.Versos[leaf.versoID], + groupIDs: this.groupIDs, + leafIDs: this.leafIDs, + Groups: this.Groups, + Notes: this.Notes, + y: this.leafYs[this.leafIDs.indexOf(leaf.id)], + isActive: this.activeLeafs.includes(leaf.id) || this.activeRectos.includes(leaf.rectoID) || this.activeVersos.includes(leaf.versoID), + customSpacings: this.customSpacings, + handleObjectClick: this.handleObjectClick, + multiplier: this.multipliers[this.leafIDs.indexOf(leaf.id)+1], + strokeColorFilter: this.strokeColorFilter, + visibleAttributes: this.visibleAttributes, + viewingMode: this.viewingMode, + openNoteDialog: this.openNoteDialog, + }); + this.paperLeaves.push(l); + this.groupLeaves.addChild(l.path); + this.groupLeaves.addChild(l.textLeafOrder); + this.groupLeaves.addChild(l.textRecto); + this.groupLeaves.addChild(l.textVerso); + this.groupLeaves.addChild(l.attachment); + this.groupLeaves.addChild(l.textNotes); + if (this.flashItems.leaves.includes(leaf.id)) { + this.flashLeaves.push(l); + } + return l; + }, + draw: function() { + // Clear existing drawn elements + this.leafYs = []; + this.groupYs = []; + this.paperLeaves = []; + this.paperGroups = []; + this.flashGroups = []; + this.flashLeaves = []; + this.groupLeaves.removeChildren(); + this.groupGroups.removeChildren(); + this.groupContainer.removeChildren(); + this.groupTacket.removeChildren(); + + // Calculate y positions of groups and leaves + let currentY = 0; + let prevRootGroupID = null; + for (let i in this.groupIDs) { + const groupID = this.groupIDs[i]; + const group = this.Groups[groupID]; + if (group.nestLevel === 1) { + if (i>0 && prevRootGroupID) { + const prevGroupsLastMember = this.getLastMember(prevRootGroupID) + const nestLevel = prevGroupsLastMember? prevGroupsLastMember.nestLevel +1 : 1; + currentY = currentY + this.spacing*(nestLevel); + } + this.groupYs.push(currentY); + currentY = this.calculateYs(group.memberIDs, currentY, this.spacing); + + if (group.memberIDs.length===0) { + currentY = currentY + this.spacing; + } + prevRootGroupID = groupID; + } + } + + // Create background Rectangle for each group + for (let groupID of this.groupIDs) { + const group = this.Groups[groupID]; + this.createGroup(group); + } + + // Create all the leaves + for (let leafID of this.leafIDs) { + this.createLeaf(this.Leafs[leafID]); + } + // Draw all leaves and set mouse event handlers + this.paperLeaves.forEach((leaf)=> { + leaf.draw(); + leaf.setMouseEventHandlers(); + }); + + // Show filter + this.showFilter(); + + // Draw other visualizations + this.drawTackets(); + this.drawSewing(); + + this.groupContainer.addChild(this.groupGroups); + this.groupContainer.addChild(this.groupLeaves); + this.groupContainer.addChild(this.groupTacket); + this.groupContainer.addChild(this.groupTacketGuide); + // Reposition the drawing + this.groupContainer.position.y += 10; + this.fitCanvas(); + }, + activateTacketTool: function(groupID, type="tacketed") { + // Remove existing tacket + this.groupTacket.removeChildren(); + this.groupTacketGuide.removeChildren(); + this.groupTacketGuideLine.removeChildren(); + + this.tacketToolIsActive = true; + if (this.tool) this.tool.remove(); + this.tool = new paper.Tool(); + this.tool.minDistance=5; + let targets = []; + + // Remove hover cursor effect on groups and leaves + this.paperGroups.forEach((group)=>group.removeMouseEventHandlers()); + this.paperLeaves.forEach((leaf)=>leaf.removeMouseEventHandlers()); + document.body.style.cursor = "crosshair"; + + this.drawTacketGuide(groupID, type); + + this.tool.onMouseDown = (event) => { + this.tacketLineDrag = new paper.Path(); + this.tacketLineDrag.strokeColor = this.strokeColorTacket; + this.tacketLineDrag.strokeWidth = 5; + this.tacketLineDrag.add(event.point); + this.groupTacketGuide.addChild(this.tacketLineDrag); + } + this.tool.onMouseUp = (event) => { + // Remove line from canvas + this.groupTacketGuide.removeChildren(); + // Reset colour of leaves + this.paperLeaves.forEach((leaf)=> { + leaf.deactivate(); + }); + this.toggleVisualizationDrawing({type: type, value: ""}); + if (targets.length>0) { + let targetLeaf1 = targets[0]; + let targetLeaf2 = targets[targets.length/2]; + this.addVisualization(targetLeaf1.leaf.parentID, type, [targetLeaf1.leaf.id, targetLeaf2.leaf.id]); + + } else { + // Redraw old visualization + if (type==="tacketed") { + this.drawTackets(); + } else { + this.drawSewing(); + } + } + } + this.tool.onMouseDrag = (event) => { + // Update line + if (!this.tacketLineDrag.segments[1]) { + this.tacketLineDrag.add(event.point); + } else { + this.tacketLineDrag.segments[1].point = event.point; + } + targets = []; + // Highlight leaves that intersect the line + const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); + targetGroup.group.memberIDs.forEach((memberID)=> { + if (memberID.charAt(0)==="L") { + const leaf = this.getLeaf(this.leafIDs.indexOf(this.Leafs[memberID].id)+1); + if (leaf.isConjoined() && (this.tacketLineDrag.getIntersections(leaf.path).length>0 || + this.tacketLineDrag.getIntersections(leaf.conjoinedLeaf().path).length>0)) { + leaf.path.strokeColor = "#ffffff"; + leaf.conjoinedLeaf().path.strokeColor = "#ffffff"; + // Add leaf to list of targets to tacket + targets.push(leaf); + } else { + leaf.deactivate(); + } + } + + }); + } + this.tool.activate(); + }, + drawTacketGuide: function(groupID, type) { + const direction = this.Groups[groupID].direction + const dirMultiplier = (!direction || direction === "left-to-right") ? 1 : -1; + const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); + const guideY = targetGroup.path.bounds.height/2; + const guideX = (!direction || direction === "left-to-right") ? targetGroup.path.bounds.left : targetGroup.path.bounds.right; + let guideLine = new paper.Path(); + guideLine.strokeColor = "#ffffff"; + guideLine.strokeWidth = 5; + guideLine.dashArray = [10,10]; + guideLine.add(new paper.Point(guideX, targetGroup.path.bounds.y + guideY+ (this.strokeWidth/2))); + guideLine.add(new paper.Point(guideX+dirMultiplier*targetGroup.path.bounds.width/3, targetGroup.path.bounds.y + guideY+(this.strokeWidth/2))); + let guideLineArrow = new paper.Path(); + guideLineArrow.strokeColor = "#ffffff"; + guideLineArrow.strokeWidth = 3; + guideLineArrow.add(guideLine.segments[1].point.x-dirMultiplier*10, guideLine.segments[1].point.y-10); + guideLineArrow.add(guideLine.segments[1].point.x, guideLine.segments[1].point.y); + guideLineArrow.add(guideLine.segments[1].point.x-dirMultiplier*10, guideLine.segments[1].point.y+10); + + let guideLineX1 = new paper.Path(); + guideLineX1.strokeColor = "#ffffff"; + guideLineX1.strokeWidth = 3; + guideLineX1.add(new paper.Point(guideX-dirMultiplier*10, guideLine.segments[0].point.y-10)); + guideLineX1.add(new paper.Point(guideX+dirMultiplier*10, guideLine.segments[0].point.y+10)); + + let guideLineX2 = new paper.Path(); + guideLineX2.strokeColor = "#ffffff"; + guideLineX2.strokeWidth = 3; + guideLineX2.add(new paper.Point(guideX-dirMultiplier*10, guideLine.segments[0].point.y+10)); + guideLineX2.add(new paper.Point(guideX+dirMultiplier*10, guideLine.segments[0].point.y-10)); + + const drawType = type==="tacketed"? "TACKET" : "SEWING"; + let guideText = new paper.PointText({ + content: "DRAW " + drawType + " LINE", + fillColor: "#000000", + fontSize: 12, + }); + const guideTextX = (!direction || direction === "left-to-right") ? guideX+20 : guideX-guideText.bounds.width-20; + guideText.set({point: [guideTextX, targetGroup.path.bounds.y+guideY-20],}) + + const guideTextRectangleX = (!direction || direction === "left-to-right") ? guideX+15 : guideX-guideText.bounds.width-35; + const guideTextRectangleWidth = (!direction || direction === "left-to-right") ? guideText.bounds.width+10 : guideText.bounds.width+30; + + let guideTextRectangle = new paper.Rectangle( + new paper.Point(guideTextRectangleX, targetGroup.path.bounds.y+guideY-35), + new paper.Size(guideTextRectangleWidth, guideText.bounds.height+5) + ); + let guideTextBackground = new paper.Path.Rectangle(guideTextRectangle); + guideTextBackground.fillColor = "rgba(255,255,255,0.75)"; + this.groupTacketGuideLine.addChild(guideLine); + this.groupTacketGuideLine.addChild(guideLineArrow); + this.tacketToolOriginalPosition = this.groupTacketGuideLine.position.x; + this.groupTacketGuide.addChild(this.groupTacketGuideLine); + this.groupTacketGuide.addChild(guideTextBackground); + this.groupTacketGuide.addChild(guideText); + this.groupTacketGuide.addChild(guideLineX1); + this.groupTacketGuide.addChild(guideLineX2); + }, + deactivateTacketTool: function() { + this.tacketToolIsActive = false; + this.groupTacketGuide.removeChildren(); + if (this.tool) { + this.tool.remove(); + } + document.body.style.cursor = "default"; + this.paperGroups.forEach((group)=>group.setMouseEventHandlers()); + this.paperLeaves.forEach((leaf)=>leaf.setMouseEventHandlers()); + }, + drawSewing: function() { + this.paperGroups.forEach((group)=> { + const dirMultiplier = (!group.direction || group.direction === "left-to-right") ? 1 : -1 + if (group.group.sewing!==null && group.group.sewing.length>0) { + const leafID1 = group.group.sewing[0]; + const leafID2 = group.group.sewing.length>1? group.group.sewing[1] : undefined; + + if (leafID1!==undefined && this.leafIDs.indexOf(leafID1)>=0) { + let startX, startY, endX, endY; + let paperLeaf1, paperLeaf2; + + paperLeaf1 = this.getLeaf(this.leafIDs.indexOf(leafID1)+1); + if (leafID2!==undefined) { + paperLeaf2 = this.getLeaf(this.leafIDs.indexOf(leafID2)+1); + startX = paperLeaf1.path.segments[0].point.x-dirMultiplier*this.strokeWidth; + startY = paperLeaf2.path.segments[0].point.y; + endX = paperLeaf2.path.segments[0].point.x; + } else { + startX = 15; + startY = paperLeaf1.path.segments[0].point.y; + endX = paperLeaf1.path.segments[0].point.x; + } + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + startY -= this.spacing*0.15; + } + endY = startY; + let sewingPath = new paper.Path(); + sewingPath.name = "tacket1"; + sewingPath.strokeColor = this.strokeColorTacket; + sewingPath.strokeWidth = 3; + sewingPath.add(new paper.Point(startX, startY)); + sewingPath.add(new paper.Point(endX+dirMultiplier*this.strokeWidth, endY)); + const that = this; + // Add listeners + sewingPath.onClick = function(event) { + that.handleObjectClick(group.group, event); + } + sewingPath.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + sewingPath.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.groupTacket.addChild(sewingPath); + } + } + }); + }, + drawTackets: function() { + this.paperGroups.forEach((group)=> { + const dirMultiplier = (!group.direction || group.direction === "left-to-right") ? 1 : -1 + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + const leafID1 = group.group.tacketed[0]; + const leafID2 =group.group.tacketed[1]; + if (leafID1!==undefined && this.leafIDs.indexOf(leafID1)>=0) { + let startX, startY, endX, endY; + let paperLeaf1, paperLeaf2; + + paperLeaf1 = this.getLeaf(this.leafIDs.indexOf(leafID1)+1); + if (leafID2!==undefined) { + paperLeaf2 = this.getLeaf(this.leafIDs.indexOf(leafID2)+1); + startX = paperLeaf1.path.segments[0].point.x-dirMultiplier*this.strokeWidth; + startY = paperLeaf2.path.segments[0].point.y; + endX = paperLeaf2.path.segments[0].point.x; + } else { + startX = 15; + startY = paperLeaf1.path.segments[0].point.y; + endX = paperLeaf1.path.segments[0].point.x; + } + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + startY -= this.spacing*0.2; + } + if (group.group.sewing!==null && group.group.sewing.length>0) { + startY += this.spacing*0.25; + } + endY = startY; + let tacketPath1 = new paper.Path(); + tacketPath1.name = "tacket1"; + tacketPath1.strokeColor = this.strokeColorTacket; + tacketPath1.strokeWidth = 3; + tacketPath1.add(new paper.Point(startX, startY-2)); + tacketPath1.add(new paper.Point(endX+dirMultiplier*this.strokeWidth, endY-2)); + tacketPath1.add(new paper.Point(tacketPath1.segments[1].point.x+dirMultiplier*5, tacketPath1.segments[1].point.y-3)); + let tacketPath2 = new paper.Path(); + tacketPath2.name = "tacket2"; + tacketPath2.strokeColor = this.strokeColorTacket; + tacketPath2.strokeWidth = 3; + tacketPath2.add(new paper.Point(startX, startY+2)); + tacketPath2.add(new paper.Point(endX+dirMultiplier*this.strokeWidth, endY+2)); + tacketPath2.add(new paper.Point(tacketPath2.segments[1].point.x+dirMultiplier*5, tacketPath2.segments[1].point.y+3)); + const that = this; + // Add listeners + tacketPath1.onClick = function(event) { + that.handleObjectClick(group.group, event); + } + tacketPath2.onClick = function(event) { + that.handleObjectClick(group.group, event); + } + tacketPath1.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + tacketPath1.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + tacketPath2.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + tacketPath2.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.groupTacket.addChild(tacketPath1); + this.groupTacket.addChild(tacketPath2); + } + } + }); + }, + getYOfFirstMember: function(groupID) { + let group = this.Groups[groupID]; + if (group.memberIDs.length===0) { + let y = this.groupYs[this.groupIDs.indexOf(group.id)]; + return y; + } + let firstMemberID = group.memberIDs[0]; + let firstMember = this[firstMemberID.split("_")[0]+"s"][firstMemberID]; + if (firstMemberID.memberType==="Group") { + return this.getYOfFirstMember(firstMemberID); + } else { + let firstLeafY = this.leafYs[this.leafIDs.indexOf(firstMember.id)]; + return firstLeafY; + } + }, + getYOfLastMember: function(groupID) { + const group = this.Groups[groupID]; + const lastMember = this.getLastMember(groupID); + if (lastMember && lastMember.memberType==="Group") { + let y = this.groupYs[this.groupIDs.indexOf(lastMember.id)]; + return y+((lastMember.nestLevel-group.nestLevel)*this.spacing); + } else if (lastMember && lastMember.memberType==="Leaf") { + let lastLeafY = this.leafYs[this.leafIDs.indexOf(lastMember.id)] + this.strokeWidth + this.spacing/2.0; + return lastLeafY+((lastMember.nestLevel-group.nestLevel-1)*this.spacing); + } else { + return 0; + } + }, + getLastMember: function(groupID) { + let lastMemberIDs = this.Groups[groupID].memberIDs; + if (lastMemberIDs.length===0) return null; + let lastMemberID = lastMemberIDs[lastMemberIDs.length-1]; + if (lastMemberID.charAt(0)==="L") { + return this.Leafs[lastMemberID]; + } else { + let lastMember = this.Groups[lastMemberID]; + // Check if this group has members + let innerLastMember = this.getLastMember(lastMemberID); + if (innerLastMember) lastMember = innerLastMember; + return lastMember; + } + }, + getGroupHeight: function(group) { + if (group.memberIDs.length>0) { + let height = this.getYOfLastMember(group.id) - this.groupYs[this.groupIDs.indexOf(group.id)]; + return height+this.spacing; + } else { + return this.spacing; + } + }, + numLeaves: function() { + return this.paperLeaves.length; + }, + getLeaf: function(leaf_order) { + return this.paperLeaves[leaf_order-1]; + }, + getLastLeaf: function() { + return this.paperLeaves[this.numLeaves()-1]; + }, + calculateYs: function(members, currentY, spacing) { + if (members.length<1) { + return currentY; + } + let multiplier = 1; + if (members.length>70) { + multiplier = 0.5; + } else if (members.length>45) { + multiplier = 0.6; + } else if (members.length>35 || this.viewingMode) { + multiplier = 0.8; + } + members.forEach((memberID, i)=> { + let memberObject = this[memberID.split("_")[0]+"s"][memberID]; + let notesToShowAbove = memberObject.notes.filter((noteID)=>{return this.Notes[noteID].show}).length; + let notesToShowBelow = 0; + let glueSpacing = 0; + if (memberObject.memberType==="Leaf") { + // Find if it has side notes + notesToShowAbove += this.Rectos[memberObject.rectoID].notes.filter((noteID)=>{return this.Notes[noteID].show}).length; + notesToShowBelow += this.Versos[memberObject.versoID].notes.filter((noteID)=>{return this.Notes[noteID].show}).length; + // Find if leaf has glue that's not a partial glue + glueSpacing = (notesToShowAbove>0 && memberObject.attached_above.includes("Glued") && !memberObject.attached_above.includes("Partial"))? 1 : 0; + } + + if (memberObject.memberType === "Leaf" && getMemberOrder(memberObject, this.Groups, this.groupIDs)===1 && notesToShowAbove>0) { + // First leaf in the group with a note + this.multipliers[this.leafIDs.indexOf(memberObject.id)+1] = multiplier; + currentY = currentY + spacing*(notesToShowAbove+1); + this.leafYs.push(currentY); + currentY = currentY + spacing*notesToShowBelow*0.8; + if (i===(members.length-1)) { + // Last member of group + currentY = currentY + (memberObject.nestLevel)*spacing; + } + } else if (memberObject.memberType==="Leaf" && this.leafIDs.indexOf(memberObject.id)+1 > 0) { + this.multipliers[this.leafIDs.indexOf(memberObject.id)+1] = multiplier; + currentY = currentY + spacing*(Math.max(1,notesToShowAbove)) + spacing*glueSpacing; + if (i > 0 && members[i-1].includes("Group") && this.Groups[members[i-1]].memberIDs.length) { + // Previous sibling is a group with children + // Find difference of nest level between current leaf and previous group's last member + const previousMember = this.getLastMember(members[i-1]); + currentY = currentY + (previousMember.nestLevel - memberObject.nestLevel)*spacing; + } + this.leafYs.push(currentY); + currentY = currentY + spacing*notesToShowBelow*0.8; + } else if (memberObject.memberType==="Group") { + currentY = currentY + spacing; + if (i > 0 && members[i-1].includes("Group") && this.Groups[members[i-1]].memberIDs.length>0) { + // Previous sibling is a group with children + const previousMember = this.getLastMember(members[i-1]); + currentY = currentY + (previousMember.nestLevel - memberObject.nestLevel + 1)*spacing; + } else if (i > 0 && members[i-1].includes("Group")&& this.Groups[members[i-1]].memberIDs.length===0) { + // Previous sibling is a group without children + currentY = currentY + spacing; + } + this.groupYs.push(currentY); + + // Recursify!!! + currentY = this.calculateYs(memberObject.memberIDs, currentY, spacing); + } + }); + return currentY; + }, + fitCanvas: function() { + // Resize canvas so that nothing is cut off + this.canvas.height = this.groupGroups.bounds.bottom+10; + }, + setWidth: function(value) { + this.width = value; + }, + setProject: function(project) { + this.groupIDs = project.groupIDs; + this.leafIDs = project.leafIDs; + this.Groups = project.Groups; + this.Leafs = project.Leafs; + this.Rectos = project.Rectos; + this.Versos = project.Versos; + this.Notes = project.Notes; + }, + setActiveGroups: function(value) { + this.activeGroups = value; + if (this.paperGroups.length>0) { + this.paperGroups.forEach((group)=>{ + group.deactivate(); + }); + this.paperGroups.forEach((paperGroup)=> { + if (this.activeGroups.includes(paperGroup.group.id)) { + paperGroup.activate(); + } + }); + } + }, + setActiveLeafs: function(value) { + this.activeLeafs = value; + if (this.paperLeaves.length>0) { + this.paperLeaves.forEach((leaf)=>{ + leaf.deactivate(); + }); + this.paperLeaves.forEach((paperLeaf)=> { + if (this.activeLeafs.includes(paperLeaf.leaf.id)) { + paperLeaf.activate(); + } + }); + } + }, + setActiveRectos: function(value) { + this.activeRectos = value; + if (this.paperLeaves.length>0) { + this.paperLeaves.forEach((leaf)=>{ + leaf.deactivate(); + }); + this.paperLeaves.forEach((paperLeaf)=> { + if (this.activeRectos.includes(paperLeaf.leaf.rectoID)) { + paperLeaf.activate(); + } + }); + } + }, + setActiveVersos: function(value) { + this.activeVersos = value; + if (this.paperLeaves.length>0) { + this.paperLeaves.forEach((leaf)=>{ + leaf.deactivate(); + }); + this.paperLeaves.forEach((paperLeaf)=> { + if (this.activeVersos.includes(paperLeaf.leaf.versoID)) { + paperLeaf.activate(); + } + }); + } + }, + setFlashItems: function(value) { + this.flashItems = value; + }, + setFilter: function(filters) { + this.filters = filters; + this.showFilter(); + }, + showFilter: function() { + this.paperLeaves.forEach((leaf)=>{ + leaf.filterHighlight.opacity = 0; + if (this.filters.Leafs.includes(leaf.leaf.id) + || this.filters.Sides.includes(leaf.leaf.rectoID) + || this.filters.Sides.includes(leaf.leaf.versoID)) { + leaf.filterHighlight.opacity = 1; + } + }); + this.paperGroups.forEach((group)=>{ + group.filterHighlight.opacity = 0; + if (this.filters.Groups.includes(group.group.id)) { + group.filterHighlight.opacity = 1; + } + }); + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + this.paperGroups.forEach((group)=>group.setVisibility(visibleAttributes.group)); + this.paperLeaves.forEach((leaf)=>leaf.setVisibility(visibleAttributes)); + }, + setScale: function(spacing, strokeWidth) { + this.spacing = this.width*spacing; + this.strokeWidth = this.width*strokeWidth; + }, +} +function PaperManager(args) { + this.canvas = document.getElementById(args.canvasID); + paper.setup(this.canvas); + this.tool = null; + this.groupIDs = args.groupIDs; + this.leafIDs = args.leafIDs; + this.Groups = args.Groups; + this.Leafs = args.Leafs; + this.Rectos = args.Rectos; + this.Versos = args.Versos; + this.Notes = args.Notes; + this.origin = args.origin; + this.width = paper.view.viewSize.width; + this.spacing = this.width*args.spacing; + this.strokeWidth = this.width*args.strokeWidth; + this.strokeColor = args.strokeColor; + this.strokeColorActive = args.strokeColorActive; + this.strokeColorAdded = args.strokeColorAdded; + this.strokeColorGroupActive = args.strokeColorGroupActive; + this.strokeColorTacket = args.strokeColorTacket; + this.groupColor = args.groupColor; + this.groupColorActive = args.groupColorActive; + this.groupTextColor = args.groupTextColor; + this.handleObjectClick = args.handleObjectClick; + this.groupLeaves = new paper.Group();// Groups of leaf paths + this.groupGroups = new paper.Group();// Group of group paths + this.groupContainer = new paper.Group(); + this.activeGroups = args.activeGroups; + this.activeLeafs = args.activeLeafs; + this.activeRectos = args.activeRectos; + this.activeVersos = args.activeVersos; + this.paperLeaves = []; + this.paperGroups = []; + this.leafYs = []; + this.groupYs = []; + this.multipliers = {}; + this.flashItems = args.flashItems; + this.flashLeaves = []; + this.flashGroups = []; + this.filters = args.filters; + this.strokeColorFilter = args.strokeColorFilter; + this.visibleAttributes = args.visibleAttributes; + this.viewingMode = args.viewingMode; + this.tacketLineDrag = new paper.Path(); + this.groupTacketGuide = new paper.Group(); + this.groupTacketGuideLine = new paper.Group(); + this.groupTacket = new paper.Group(); + this.toggleVisualizationDrawing = args.toggleVisualizationDrawing; + this.addVisualization = args.addVisualization; + this.tacketToolIsActive = false; + this.tacketToolOriginalPosition = 0; + this.slideForward = true; + this.openNoteDialog = args.openNoteDialog; + this.leafIDs = args.leafIDs; + + let that = this; + // Flash newly added items + paper.view.onFrame = function(event) { + for (let i=0; i that.tacketToolOriginalPosition+25) { + that.slideForward = false; + } + } else { + that.groupTacketGuideLine.position.x -= 0.5; + if (that.groupTacketGuideLine.position.x < that.tacketToolOriginalPosition) { + that.slideForward = true; + } + } + } + } +} +export default PaperManager; diff --git a/viscoll-app/src/assets/visualMode/export/PaperGroup.js b/viscoll-app/src/assets/visualMode/export/PaperGroup.js new file mode 100644 index 00000000..bea8153e --- /dev/null +++ b/viscoll-app/src/assets/visualMode/export/PaperGroup.js @@ -0,0 +1,129 @@ +import paper from 'paper'; + +PaperGroup.prototype = { + constructor: PaperGroup, + draw: function() { + // Create rectangle shapes + const rightIndent = (this.direction === "left-to-right") ? 0 : (this.spacing*(this.nestLevel-1)); + let rectangle = new paper.Rectangle( + new paper.Point(this.x+10, this.y), + new paper.Size(this.width-this.x-rightIndent-20, this.groupHeight) + ); + if (this.viewingMode) { + rectangle = new paper.Rectangle( + new paper.Point(this.x+10, this.y), + new paper.Size(this.width-rightIndent-this.x, this.groupHeight) + ); + } + let highlightRectangle = rectangle.clone(); + highlightRectangle.set( + new paper.Point(this.x, this.y-10), + new paper.Size(this.width-rightIndent-this.x, this.groupHeight+20) + ); + + // Create path from rectangle + this.path = new paper.Path.Rectangle(rectangle); + if (this.isActive) { + this.path.fillColor = this.groupColorActive; + } else { + this.path.fillColor = this.groupColor; + } + if (this.group.nestLevel%2===0) { + this.path.fillColor.brightness -= 0.05; + } + this.path.name = "group " + this.groupOrder; + + // Create highlight path from rectangle + this.highlight = new paper.Path.Rectangle(highlightRectangle); + this.highlight.fillColor = new paper.Color(112/255.0, 229/255.0, 220/255.0, 1); + this.highlight.opacity = 0; + this.highlight.name = "group " + this.groupOrder + " highlight"; + this.highlight.insertBelow(this.path); + + this.filterHighlight = new paper.Path.Rectangle(highlightRectangle); + this.filterHighlight.fillColor = this.filterColor; + this.filterHighlight.opacity = 0; + this.filterHighlight.insertBelow(this.path); + + // Set highlight path to be at the back + paper.project.activeLayer.insertChild(0, this.highlight); + paper.project.activeLayer.insertChild(0, this.filterHighlight); + }, + setMouseEventHandlers: function() { + // Set mouse event handlers + let that = this; + this.path.onClick = function(event) { + that.handleObjectClick(that.group, event); + }; + this.path.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.path.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + }, + removeMouseEventHandlers: function() { + this.path.onClick = function(event) {}; + this.path.onMouseEnter = function(event) {}; + this.path.onMouseLeave = function(event) {}; + }, + activate: function() { + this.path.fillColor = this.groupColorActive; + this.isActive = true; + this.highlight.opacity = 0.75; + this.highlight.fillColor = "#ffffff"; + }, + deactivate: function() { + this.path.fillColor = this.groupColor; + if (this.group.nestLevel%2===0) { + this.path.fillColor.brightness -= 0.05; + } + this.isActive = false; + this.highlight.opacity = 0; + this.highlight.fillColor = new paper.Color(112/255.0, 229/255.0, 220/255.0, 1); + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + let groupText = this.group.type + " " + this.groupOrder; + if (this.visibleAttributes && this.visibleAttributes.title) groupText = groupText + ": " + this.group.title; + this.text.set({ + content: groupText, + }); + if(this.direction === "right-to-left"){ + this.text.set({ + point: [this.width-this.text.bounds.width-this.text.point.x-(this.spacing*(this.nestLevel-1)), this.text.point.y], + }) + } + }, +} +// Constructor for group +function PaperGroup(args) { + this.manager = args.manager; + this.group = args.group; + this.groupOrder = args.allGroupIDs.indexOf(args.group.id)+1 + this.direction = args.direction; + this.y = args.y; + this.x = args.x; + this.nestLevel = args.nestLevel; + this.spacing = args.spacing; + this.width = args.width; + this.groupHeight = args.groupHeight; + this.isActive = args.isActive; + this.highlight = new paper.Path(); + this.filterHighlight = new paper.Path(); + this.path = new paper.Path(); + this.groupColor = args.groupColor; + this.groupColorActive = args.groupColorActive; + this.textColor = args.textColor; + this.filterColor = args.filterColor; + this.handleObjectClick = args.handleObjectClick; + this.visibleAttributes = {}; + this.viewingMode = args.viewingMode; + this.text = new paper.PointText({ + point: [this.x+args.spacing*0.6, this.y+args.spacing*0.6], + fillColor: this.textColor, + fontSize: args.spacing*0.48, + }); + this.setVisibility(args.visibleAttributes); +} +export default PaperGroup; diff --git a/viscoll-app/src/assets/visualMode/export/PaperLeaf.js b/viscoll-app/src/assets/visualMode/export/PaperLeaf.js new file mode 100644 index 00000000..6542ee1b --- /dev/null +++ b/viscoll-app/src/assets/visualMode/export/PaperLeaf.js @@ -0,0 +1,643 @@ +import paper from 'paper'; +PaperLeaf.prototype = { + constructor: PaperLeaf, + draw: function() { + // Call this function only after ALL leaves have been instantiated + // This is because we need all leaves present in order + // to compute their indentations relative to each other + this.setIndent(); + // Draw horizontal part + let x1, x2 + + if(!this.groupDirection || this.groupDirection === "left-to-right"){ + x1 = this.multiplier<1? 10 + this.indent*this.spacing*this.multiplier : this.indent*this.spacing*this.multiplier; + x2 = this.width; + } else { + x1 = this.groupWidth - (this.multiplier < 1 ? 10 + this.indent*this.spacing*this.multiplier : this.indent*this.spacing*this.multiplier); + x2 = this.groupWidth - this.width; + this.oldPointX = x2; + } + this.dirMultiplier = (!this.groupDirection || this.groupDirection === "left-to-right") ? 1 : -1; + + this.path.add(new paper.Point(x1, this.y)); + if (this.leaf.stub !== "None" && (!this.groupDirection || this.groupDirection === "left-to-right")) { + x2 = this.width*0.15+x1; + } else if (this.leaf.stub !== "None") { + x2 = (x1-this.width*0.15)-x2; + } + + this.path.add(new paper.Point(x2, this.y)); + // Draw vertical part + if (this.isConjoined()) { + var conjoinY=this.y_conjoin_center(this.conjoined_to); + this.path.insert(0, new paper.Point(this.path.segments[0].point.x, conjoinY)); + } + this.curveMe(); + + this.path.name = "leaf " + this.order; + + // Create highlight path + this.highlight = this.path.clone(); + this.highlight.dashArray = [0, 0]; + this.highlight.segments[this.highlight.segments.length-1].point.x = this.highlight.segments[this.highlight.segments.length-1].point.x + 5; + if (this.leaf.conjoined_to === "None") { + this.highlight.segments[0].point.x = this.highlight.segments[0].point.x - 5; + } + this.highlight.strokeColor = this.strokeColorActive; + this.highlight.strokeWidth = this.path.strokeWidth*2; + this.highlight.opacity = 0; + this.highlight.name = "leaf " + this.order + " highlight"; + this.highlight.insertBelow(this.path); + + if (this.isActive) { + this.highlight.opacity = 0.35; + this.highlight.strokeWidth = this.path.strokeWidth*2; + this.highlight.strokeColor = "#ffffff"; + } + + this.filterHighlight = this.path.clone(); + this.filterHighlight.dashArray = [0, 0]; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x + 5; + if (this.leaf.conjoined_to === "None") { + this.filterHighlight.segments[0].point.x = this.filterHighlight.segments[0].point.x - 5; + } + this.filterHighlight.strokeColor = this.strokeColorFilter; + this.filterHighlight.strokeWidth = this.path.strokeWidth*2; + this.filterHighlight.opacity = 0; + this.filterHighlight.insertBelow(this.path); + + // this.path.fullySelected = true; + this.showAttributes(); + this.createAttachments(); + + const leafNotesToShow = this.leaf.notes.filter((noteID)=>{return this.Notes[noteID].show}).reverse(); + const rectoNotesToShow = this.recto.notes.filter((noteID)=>{return this.Notes[noteID].show}).reverse(); + const versoNotesToShow = this.verso.notes.filter((noteID)=>{return this.Notes[noteID].show}); + + let textX = 0; + let textY = this.y; + let fontSize = this.spacing*0.50; + let numChars = this.path.bounds.width/fontSize*2.4; + + if (this.isConjoined()) { + // This leaf is conjoined + textX = this.path.segments[1].point.x; + } else { + // Separate leaf + textX = this.path.segments[0].point.x+this.dirMultiplier*10; + } + if (this.leaf.attached_above.includes("Partial")) { + // This leaf has a partial glue attachment + // Place text to the right of attachment drawing + if(!this.groupDirection || this.groupDirection === "left-to-right"){ + textX = this.attachment.bounds.right+5; + } else { + textX = this.attachment.bounds.left - 5; + } + } else if (this.leaf.attached_above.includes("Glued")) { + // Other type of glueing exists + if(!this.groupDirection || this.groupDirection === "left-to-right"){ + textY -= this.spacing*0.7; + } else { + textY += this.spacing*0.7; + } + } + let that = this; + let clickListener = function(note) { + return function(event) { + that.openNoteDialog(note); + } + } + if (this.showNotes) { + // Draw recto note text + for (let noteIndex = 0; noteIndex < rectoNotesToShow.length; noteIndex++) { + const note = this.Notes[rectoNotesToShow[noteIndex]]; + const noteTitle = this.recto.folio_number? "â–¼ " + this.recto.folio_number + " : " + note.title.substr(0,numChars) : "â–¼ R : " + note.title.substr(0,numChars) ; + let textNote = new paper.PointText({ + content: noteTitle, + point: [textX, textY - noteIndex*(this.spacing*0.7) - this.spacing*0.3], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + const textPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? textX : textX-textNote.bounds.width; + textNote.set({point: [textPointX, textY - noteIndex * (this.spacing * 0.7) - this.spacing * 0.3]}); + textNote.onClick = !this.viewingMode && clickListener(note); + this.textNotes.addChild(textNote); + } + // Draw leaf note text + for (let noteIndex = 0; noteIndex < leafNotesToShow.length; noteIndex++) { + const note = this.Notes[leafNotesToShow[noteIndex]]; + + let textNote = new paper.PointText({ + content: "â–¼ L" + this.order + " : " + note.title.substr(0,numChars), + fillColor: this.strokeColor, + fontSize: fontSize, + }); + const textPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? textX : textX - textNote.bounds.width; + textNote.set({point: [textPointX, textY - rectoNotesToShow.length*(this.spacing*0.7) - noteIndex*(this.spacing*0.7) - this.spacing*0.3]}); + textNote.onClick = !this.viewingMode && clickListener(note); + this.textNotes.addChild(textNote); + } + // Draw verso note text + for (let noteIndex = 0; noteIndex < versoNotesToShow.length; noteIndex++) { + const note = this.Notes[versoNotesToShow[noteIndex]]; + const noteTitle = this.verso.folio_number? "â–² " + this.verso.folio_number + " : " + note.title.substr(0,numChars) : "â–² V : " + note.title.substr(0,numChars); + let textNote = new paper.PointText({ + content: noteTitle, + point: [textX, this.y + noteIndex*(this.spacing*0.7) + this.spacing*0.8], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + const textPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? textX : textX-textNote.bounds.width; + textNote.set({point: [textPointX, this.y + noteIndex*(this.spacing*0.7) + this.spacing*0.8]}) + textNote.onClick = !this.viewingMode && clickListener(note); + this.textNotes.addChild(textNote); + } + this.textNotes.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textNotes.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + } + + + return this; + }, + setMouseEventHandlers: function() { + // Set mouse event handlers + let that = this; + this.path.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.textLeafOrder.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.textRecto.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.textVerso.onClick = function(event) { + that.handleObjectClick(that.leaf, event); + }; + this.path.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.path.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.textLeafOrder.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textLeafOrder.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.textRecto.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textRecto.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.textVerso.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + this.textVerso.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + }, + removeMouseEventHandlers: function() { + // Set mouse event handlers + this.path.onClick = function(event) {}; + this.textLeafOrder.onClick = function(event) {}; + this.textRecto.onClick = function(event) {}; + this.textVerso.onClick = function(event) {}; + this.path.onMouseEnter = function(event) {}; + this.path.onMouseLeave = function(event) {}; + this.textLeafOrder.onMouseEnter = function(event) {}; + this.textLeafOrder.onMouseLeave = function(event) {}; + this.textRecto.onMouseEnter = function(event) {}; + this.textRecto.onMouseLeave = function(event) {}; + this.textVerso.onMouseEnter = function(event) {}; + this.textVerso.onMouseLeave = function(event) {}; + }, + createAttachments: function() { + if (this.order>1 && this.leaf.attached_above.includes("Glued")) { + this.createGlue(); + } else if (this.order>1 && this.leaf.attached_above==="Other") { + this.createOtherAttachment(); + } + if (this.order>1 && this.leaf.conjoin_type==="Sewn") { + this.createSewn(); + } + }, + createGlue: function() { + let x = this.path.segments[0].point.x; + if (this.isConjoined() && this.conjoined_to (this.path.segments[this.path.segments.length-1].point.x-10)) { + let glueLine = new paper.Path(); + glueLine.add(new paper.Point(x, this.y-this.spacing*0.3)); + glueLine.add(new paper.Point(x-10, this.y-this.spacing*0.7)); + glueLine.strokeColor = "#707070"; + glueLine.strokeWidth = 2; + this.attachment.addChild(glueLine); + x -= 5; + } + } else { + // Complete glue + while (x <= (this.path.segments[this.path.segments.length-1].point.x-10)) { + let glueLine = new paper.Path(); + glueLine.add(new paper.Point(x, this.y-this.spacing*0.3)); + glueLine.add(new paper.Point(x+10, this.y-this.spacing*0.7)); + glueLine.strokeColor = "#707070"; + glueLine.strokeWidth = 2; + this.attachment.addChild(glueLine); + x += 5; + } + } + }, + // Sewing of conjoined leaves + createSewn: function() { + if (this.isConjoined() && this.conjoined_to0; + }, + conjoinedLeaf: function() { + return this.manager.getLeaf(this.conjoined_to); + }, + y_conjoin_center: function(conjoin_order) { + var y1 = this.path.segments[1].point.y; + var y2 = (this.manager.getLeaf(conjoin_order)).getY(); + return y1+((y2-y1)/2.0); + }, + y_nonconjoin_center: function() { + var y = this.y + var y_center = this.y_centerQuire(); + if (y===y_center) { + return y_center; + } else if (y= this.manager.numLeaves()) { + return 0; + } else { + return this.manager.getLeaf(this.order+1).y; + } + }, + curveMe: function() { + var path_height = Math.abs(this.path.segments[0].point.y - this.path.segments[1].point.y); + var midpoint = this.path.segments[1].point; + if (this.isConjoined() ) { + var numLeavesInside = Math.abs(this.conjoined_to - this.order); + // Remove the middle point and insert a new one with handles + this.path.removeSegment(1); + // // Calculate new point's location and radius + var new_x = midpoint.x + this.dirMultiplier*20; + var radius = new_x - this.path.segments[0].point.x; + var s1 = new paper.Segment(new paper.Point(new_x, midpoint.y), new paper.Point(-radius,0), null); + this.path.insert(1, s1); + + if (numLeavesInside > 5) { + // Modify first point's handle so that the curve isn't too curvy + var direction = this.path.segments[0].point.y > this.path.segments[1].point.y? -1 : 1; + var oldPoint = this.path.segments[0].point; + this.path.removeSegment(0); + var s0 = new paper.Segment(new paper.Point(oldPoint.x, oldPoint.y), null, new paper.Point(0,direction*(path_height))); + this.path.insert(0, s0); + } + } + }, + setIndent: function() { + this.indent = this.leaf.nestLevel; + if (this.isConjoined() && this.conjoinedLeaf().indent != null) { + // Leaf is conjoined and conjoiner has indent, so copy that conjoiner's indent + this.indent = this.conjoinedLeaf().indent; + } else if (this.isBelowAConjoined()) { + this.indent = (this.prevPaperLeaf().indent+1); + } else if (this.localOrder>1 && this.parentOrder === this.prevPaperLeaf().parentOrder){ + // Leaf is a sibling of previous leaf, so copy sibling's indent + this.indent = this.prevPaperLeaf().indent; + } + }, + isBelowAConjoined: function() { + let previousLeafID = this.allLeafIDs[this.allLeafIDs.indexOf(this.leaf.id)-1]; + if (!previousLeafID) return false; + let previousLeaf = this.Leafs[previousLeafID]; + return (this.order>1 && previousLeaf.conjoined_to!==null && (this.allLeafIDs.indexOf(previousLeaf.conjoined_to)+1)>this.order) + }, + y_centerQuire: function () { + var last_leaf = this.manager.getLastLeaf().getY(); + return (last_leaf+ this.spacing)/2.0 ; + }, + getY: function() { + return this.y; + }, + activate: function() { + this.path.strokeColor = this.strokeColorActive; + this.highlight.opacity = 0.35; + this.highlight.strokeWidth = this.path.strokeWidth*2; + this.highlight.strokeColor = "#ffffff"; + }, + deactivate: function() { + this.highlight.opacity = 0; + this.highlight.strokeWidth = this.path.strokeWidth; + this.highlight.strokeColor = this.strokeColorActive; + + if (this.leaf.type==="Added") { + this.path.strokeColor = this.strokeColorAdded; + } else if (this.leaf.type==="Endleaf") { + this.path.strokeColor = "#727272"; + } else { + this.path.strokeColor = this.strokeColor; + } + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + this.showAttributes(); + }, + showAttributes: function() { + const rectoValues = []; + const versoValues = []; + if (this.visibleAttributes.side) { + if (this.visibleAttributes.side.folio_number) { + if (this.recto.folio_number) rectoValues.push(this.recto.folio_number) + if (this.verso.folio_number) versoValues.push(this.verso.folio_number) + } + if (this.visibleAttributes.side.page_number) { + if (this.recto.page_number) rectoValues.push(this.recto.page_number) + if (this.verso.page_number) versoValues.push(this.verso.page_number) + } + if (this.visibleAttributes.side.texture) { + rectoValues.push(this.recto.texture) + versoValues.push(this.verso.texture) + } + } + let rectoContent = ""; + let versoContent = ""; + for (let i=0; i { + if (this.visibleAttributes.side[key]) return acc+1; + return acc; + } + const visibleAttributeCount = this.visibleAttributes.side? Object.keys(this.visibleAttributes.side).reduce(reducer,0) : 0; + + if (visibleAttributeCount===3) { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*3 : this.oldPointX + this.spacing * 3; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2.8 : this.oldPointX + this.spacing * 2.8; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2.8 : this.oldPointX + this.spacing * 2.8; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2.8 : this.oldPointX - this.textLeafOrder.bounds.width + this.spacing * 2.8; + + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit attribute text + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + this.textRecto.set({content: rectoContent}); + this.textVerso.set({content: versoContent}); + + const textRectoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.4 : this.textLeafOrder.bounds.left - this.textRecto.bounds.width - this.spacing * 0.4; + const textVersoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.4 :this.textLeafOrder.bounds.left - this.textVerso.bounds.width - this.spacing * 0.4; + + this.textRecto.set({ point: [textRectoX, this.textRecto.point.y], }); + this.textVerso.set({ point: [textVersoX, this.textVerso.point.y], }); + } else if (visibleAttributeCount===2) { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*2 : this.oldPointX + this.spacing * 2; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*1.8 : this.oldPointX + this.spacing * 1.8; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*1.8 : this.oldPointX + this.spacing * 1.8; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*1.8 : this.oldPointX- this.textLeafOrder.bounds.width + this.spacing * 1.8; + + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit attribute text + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + this.textRecto.set({content: rectoContent}); + this.textVerso.set({content: versoContent}); + + const textRectoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textRecto.bounds.width - this.spacing * 0.2; + const textVersoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textVerso.bounds.width - this.spacing * 0.2; + + this.textRecto.set({ point: [textRectoX, this.textRecto.point.y], }); + this.textVerso.set({ point: [textVersoX, this.textVerso.point.y], }); + } + else if (visibleAttributeCount===1) { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing : this.oldPointX + this.spacing; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*0.8 : this.oldPointX + this.spacing * 0.8; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*0.8 : this.oldPointX + this.spacing * 0.8; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width-this.spacing*0.8 : this.oldPointX- this.textLeafOrder.bounds.width + this.spacing * 0.8; + + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit folio number text + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + this.textRecto.set({content: rectoContent}); + this.textVerso.set({content: versoContent}); + + const textRectoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textRecto.bounds.width - this.spacing * 0.2; + const textVersoX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.textLeafOrder.bounds.right+this.spacing*0.2 : this.textLeafOrder.bounds.left - this.textVerso.bounds.width - this.spacing * 0.2; + + this.textRecto.set({ point: [textRectoX, this.textRecto.point.y], }); + this.textVerso.set({ point: [textVersoX, this.textVerso.point.y], }); + } else { + const newPointX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width : this.oldPointX; + const newHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width + 5 : this.oldPointX - 5; + const newfilterHighlightX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width + 5 : this.oldPointX - 5; + const textLeafOrderX = (!this.groupDirection || this.groupDirection === "left-to-right") ? this.width+10 : this.oldPointX - this.textLeafOrder.bounds.width - 5; + + // Reset leaf + if (this.leaf.stub === "None") { + this.path.segments[this.path.segments.length-1].point.x = newPointX; + this.highlight.segments[this.highlight.segments.length-1].point.x = newHighlightX; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = newfilterHighlightX; + } + this.textLeafOrder.set({point: [textLeafOrderX, this.textLeafOrder.point.y]}); + } + }, +} +// Constructor for leaf +function PaperLeaf(args) { + this.manager = args.manager; + this.Notes = args.Notes; + this.leafIDs = args.leafIDs; + this.allLeafIDs = args.allLeafIDs; + this.Leafs = args.Leafs; + this.leaf = args.leaf; + this.recto = args.recto; + this.verso = args.verso; + this.order = args.allLeafIDs.indexOf(args.leaf.id) + 1; + this.localOrder = args.leafIDs.indexOf(args.leaf.id) + 1; + this.parentOrder = args.groupIDs.indexOf(this.leaf.parentID)+1; + this.conjoined_to = this.leaf.conjoined_to===null ? "None" : this.leafIDs.indexOf(this.leaf.conjoined_to)+1; + this.indent = null; + this.origin = args.origin; + this.viewingMode = args.viewingMode; + this.width = this.viewingMode? args.width*0.88 : args.width*0.92; + this.groupDirection = args.Groups[this.leaf.parentID].direction; + this.groupWidth = args.width; + this.spacing = args.spacing; + this.y = args.y; + this.strokeWidth = args.strokeWidth; + this.strokeColor = args.strokeColor; + this.strokeColorActive = args.strokeColorActive; + this.strokeColorGroupActive = args.strokeColorGroupActive; + this.strokeColorFilter = args.strokeColorFilter; + this.strokeColorAdded = args.strokeColorAdded; + this.highlight = new paper.Path(); + this.filterHighlight = new paper.Path(); + this.path = new paper.Path(); + this.isActive = args.isActive; + this.handleObjectClick = args.handleObjectClick; + this.multiplier = args.multiplier; + this.attachment = new paper.Group(); + this.textNotes = new paper.Group(); + this.openNoteDialog = args.openNoteDialog; + this.showNotes = args.showNotes; + + this.textLeafOrder = new paper.PointText({ + point: [this.width, this.y+5], + content: "L"+ this.order, + fillColor: this.strokeColor, + fontSize: this.spacing*0.48, + }); + this.textRecto = new paper.PointText({ + point: [this.width+this.spacing, this.y-3], + fillColor: this.strokeColor, + fontSize: this.spacing*0.37, + }); + this.textVerso = new paper.PointText({ + point: [this.width+this.spacing, this.y+this.spacing*0.37-3], + fillColor: this.strokeColor, + fontSize: this.spacing*0.37, + }); + + this.visibleAttributes = args.visibleAttributes; + // Set path properties + if (this.leaf.type==="Added") { + this.path.strokeColor = args.strokeColorAdded; + } else if (this.leaf.type==="Endleaf") { + this.path.strokeColor = "#919191"; + } else { + this.path.strokeColor = args.strokeColor; + } + if (this.leaf.type==='Missing') { + // If leaf is missing, make stroke dashed + this.path.dashArray = [20, 10]; + } + if (this.leaf.type==='Replaced') { + this.path.strokeColor = "#9d6464"; + } + if (this.isActive) { + this.path.strokeColor = args.strokeColorActive; + } + this.path.strokeWidth = this.strokeWidth; + if (this.leaf.material === "Parchment") { + this.path.strokeWidth = this.path.strokeWidth*1.20; + } else if (this.leaf.material === "Paper") { + this.path.strokeWidth = this.path.strokeWidth*0.80; + } +} + + +export default PaperLeaf; diff --git a/viscoll-app/src/assets/visualMode/export/PaperManager.js b/viscoll-app/src/assets/visualMode/export/PaperManager.js new file mode 100644 index 00000000..e7193e0e --- /dev/null +++ b/viscoll-app/src/assets/visualMode/export/PaperManager.js @@ -0,0 +1,721 @@ +import paper from 'paper'; +import PaperLeaf from "./PaperLeaf.js"; +import PaperGroup from "./PaperGroup.js"; +import { getMemberOrder } from '../../../helpers/getMemberOrder'; + +PaperManager.prototype = { + constructor: PaperManager, + createGroup: function(group) { + let g = new PaperGroup({ + manager: this, + group: group, + allGroupIDs: this.allGroupIDs, + direction: group.direction, + y: this.groupYs[this.groupIDs.indexOf(group.id)], + x: (group.direction === "left-to-right") ? (group.nestLevel-1)*(this.spacing) : 0, + width: this.width, + groupHeight: this.getGroupHeight(group), + isActive: this.activeGroups.includes(group.id), + groupColor: this.groupColor, + groupColorActive: this.groupColorActive, + filterColor: this.strokeColorFilter, + handleObjectClick: this.handleObjectClick, + textColor: this.groupTextColor, + visibleAttributes: this.visibleAttributes.group, + viewingMode: this.viewingMode, + spacing: this.spacing, + nestLevel: group.nestLevel, + }); + g.draw(); + g.setMouseEventHandlers(); + + // Add this group to collections + this.groupGroups.addChild(g.filterHighlight); + this.groupGroups.addChild(g.highlight); + this.groupGroups.addChild(g.path); + this.groupGroups.addChild(g.text); + this.paperGroups.push(g); + + // Add this group to list of items to flash if it's in the flashItems list + if (this.flashItems.groups.includes(group.id)) { + this.flashGroups.push(g); + } + + }, + createLeaf: function(leaf) { + let l = new PaperLeaf({ + manager: this, + origin: this.origin, + width: this.width, + spacing: this.spacing, + strokeWidth: this.strokeWidth * Math.min(1.0, this.multipliers[this.leafIDs.indexOf(leaf.id)+1]), + strokeColor: this.strokeColor, + strokeColorActive: this.strokeColorActive, + strokeColorGroupActive: this.strokeColorGroupActive, + strokeColorAdded: this.strokeColorAdded, + leaf: leaf, + recto: this.Rectos[leaf.rectoID], + verso: this.Versos[leaf.versoID], + groupIDs: this.groupIDs, + leafIDs: this.leafIDs, + allLeafIDs: this.allLeafIDs, + Groups: this.Groups, + Leafs: this.Leafs, + Notes: this.Notes, + y: this.leafYs[this.leafIDs.indexOf(leaf.id)], + isActive: this.activeLeafs.includes(leaf.id) || this.activeRectos.includes(leaf.rectoID) || this.activeVersos.includes(leaf.versoID), + customSpacings: this.customSpacings, + handleObjectClick: this.handleObjectClick, + multiplier: this.multipliers[this.leafIDs.indexOf(leaf.id)+1], + strokeColorFilter: this.strokeColorFilter, + visibleAttributes: this.visibleAttributes, + viewingMode: this.viewingMode, + openNoteDialog: this.openNoteDialog, + showNotes: this.showNotes, + }); + this.paperLeaves.push(l); + this.groupLeaves.addChild(l.path); + this.groupLeaves.addChild(l.textLeafOrder); + this.groupLeaves.addChild(l.textRecto); + this.groupLeaves.addChild(l.textVerso); + this.groupLeaves.addChild(l.attachment); + this.groupLeaves.addChild(l.textNotes); + if (this.flashItems.leaves.includes(leaf.id)) { + this.flashLeaves.push(l); + } + return l; + }, + draw: function() { + // Clear existing drawn elements + this.leafYs = []; + this.groupYs = []; + this.paperLeaves = []; + this.paperGroups = []; + this.flashGroups = []; + this.flashLeaves = []; + this.groupLeaves.removeChildren(); + this.groupGroups.removeChildren(); + this.groupContainer.removeChildren(); + this.groupTacket.removeChildren(); + + // Calculate y positions of groups and leaves + let currentY = 0; + let prevRootGroupID = null; + for (let i in this.groupIDs) { + const groupID = this.groupIDs[i]; + const group = this.Groups[groupID]; + if (group.nestLevel === 1) { + currentY = 0; + if (i>0 && prevRootGroupID) { + const prevGroupsLastMember = this.getLastMember(prevRootGroupID) + const nestLevel = prevGroupsLastMember? prevGroupsLastMember.nestLevel +1 : 1; + currentY = currentY + this.spacing*(nestLevel); + } + this.groupYs.push(currentY); + currentY = this.calculateYs(group.memberIDs, currentY, this.spacing); + + if (group.memberIDs.length===0) { + currentY = currentY + this.spacing; + } + prevRootGroupID = groupID; + } + } + + // Create background Rectangle for each group + for (let groupID of this.groupIDs) { + const group = this.Groups[groupID]; + this.createGroup(group); + } + + // Create all the leaves + for (let leafID of this.leafIDs) { + this.createLeaf(this.Leafs[leafID]); + } + // Draw all leaves and set mouse event handlers + this.paperLeaves.forEach((leaf)=> { + leaf.draw(); + leaf.setMouseEventHandlers(); + }); + + // Show filter + this.showFilter(); + + // Draw other visualizations + this.drawTackets(); + this.drawSewing(); + + this.groupContainer.addChild(this.groupGroups); + this.groupContainer.addChild(this.groupLeaves); + this.groupContainer.addChild(this.groupTacket); + this.groupContainer.addChild(this.groupTacketGuide); + // Reposition the drawing + this.groupContainer.position.y += 10; + this.fitCanvas(); + }, + activateTacketTool: function(groupID, type="tacketed") { + // Remove existing tacket + this.groupTacket.removeChildren(); + this.groupTacketGuide.removeChildren(); + this.groupTacketGuideLine.removeChildren(); + + this.tacketToolIsActive = true; + if (this.tool) this.tool.remove(); + this.tool = new paper.Tool(); + this.tool.minDistance=5; + let targets = []; + + // Remove hover cursor effect on groups and leaves + this.paperGroups.forEach((group)=>group.removeMouseEventHandlers()); + this.paperLeaves.forEach((leaf)=>leaf.removeMouseEventHandlers()); + document.body.style.cursor = "crosshair"; + + this.drawTacketGuide(groupID, type); + + this.tool.onMouseDown = (event) => { + this.tacketLineDrag = new paper.Path(); + this.tacketLineDrag.strokeColor = this.strokeColorTacket; + this.tacketLineDrag.strokeWidth = 5; + this.tacketLineDrag.add(event.point); + this.groupTacketGuide.addChild(this.tacketLineDrag); + } + this.tool.onMouseUp = (event) => { + // Remove line from canvas + this.groupTacketGuide.removeChildren(); + // Reset colour of leaves + this.paperLeaves.forEach((leaf)=> { + leaf.deactivate(); + }); + this.toggleVisualizationDrawing({type: type, value: ""}); + if (targets.length>0) { + let targetLeaf1 = targets[0]; + let targetLeaf2 = targets[targets.length/2]; + this.addVisualization(targetLeaf1.leaf.parentID, type, [targetLeaf1.leaf.id, targetLeaf2.leaf.id]); + + } else { + // Redraw old visualization + if (type==="tacketed") { + this.drawTackets(); + } else { + this.drawSewing(); + } + } + } + this.tool.onMouseDrag = (event) => { + // Update line + if (!this.tacketLineDrag.segments[1]) { + this.tacketLineDrag.add(event.point); + } else { + this.tacketLineDrag.segments[1].point = event.point; + } + targets = []; + // Highlight leaves that intersect the line + const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); + targetGroup.group.memberIDs.forEach((memberID)=> { + if (memberID.charAt(0)==="L") { + const leaf = this.getLeaf(this.leafIDs.indexOf(this.Leafs[memberID].id)+1); + if (leaf.isConjoined() && (this.tacketLineDrag.getIntersections(leaf.path).length>0 || + this.tacketLineDrag.getIntersections(leaf.conjoinedLeaf().path).length>0)) { + leaf.path.strokeColor = "#ffffff"; + leaf.conjoinedLeaf().path.strokeColor = "#ffffff"; + // Add leaf to list of targets to tacket + targets.push(leaf); + } else { + leaf.deactivate(); + } + } + + }); + } + this.tool.activate(); + }, + drawTacketGuide: function(groupID, type) { + const direction = this.Groups[groupID].direction + const dirMultiplier = (!direction || direction === "left-to-right") ? 1 : -1; + const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); + const guideY = targetGroup.path.bounds.height/2; + const guideX = (!direction || direction === "left-to-right") ? targetGroup.path.bounds.left : targetGroup.path.bounds.right; + let guideLine = new paper.Path(); + guideLine.strokeColor = "#ffffff"; + guideLine.strokeWidth = 5; + guideLine.dashArray = [10,10]; + guideLine.add(new paper.Point(guideX, targetGroup.path.bounds.y + guideY+ (this.strokeWidth/2))); + guideLine.add(new paper.Point(guideX+dirMultiplier*targetGroup.path.bounds.width/3, targetGroup.path.bounds.y + guideY+(this.strokeWidth/2))); + let guideLineArrow = new paper.Path(); + guideLineArrow.strokeColor = "#ffffff"; + guideLineArrow.strokeWidth = 3; + guideLineArrow.add(guideLine.segments[1].point.x-dirMultiplier*10, guideLine.segments[1].point.y-10); + guideLineArrow.add(guideLine.segments[1].point.x, guideLine.segments[1].point.y); + guideLineArrow.add(guideLine.segments[1].point.x-dirMultiplier*10, guideLine.segments[1].point.y+10); + + let guideLineX1 = new paper.Path(); + guideLineX1.strokeColor = "#ffffff"; + guideLineX1.strokeWidth = 3; + guideLineX1.add(new paper.Point(guideX-dirMultiplier*10, guideLine.segments[0].point.y-10)); + guideLineX1.add(new paper.Point(guideX+dirMultiplier*10, guideLine.segments[0].point.y+10)); + + let guideLineX2 = new paper.Path(); + guideLineX2.strokeColor = "#ffffff"; + guideLineX2.strokeWidth = 3; + guideLineX2.add(new paper.Point(guideX-dirMultiplier*10, guideLine.segments[0].point.y+10)); + guideLineX2.add(new paper.Point(guideX+dirMultiplier*10, guideLine.segments[0].point.y-10)); + + const drawType = type==="tacketed"? "TACKET" : "SEWING"; + let guideText = new paper.PointText({ + content: "DRAW " + drawType + " LINE", + fillColor: "#000000", + fontSize: 12, + }); + const guideTextX = (!direction || direction === "left-to-right") ? guideX+20 : guideX-guideText.bounds.width-20; + guideText.set({point: [guideTextX, targetGroup.path.bounds.y+guideY-20],}) + + const guideTextRectangleX = (!direction || direction === "left-to-right") ? guideX+15 : guideX-guideText.bounds.width-35; + const guideTextRectangleWidth = (!direction || direction === "left-to-right") ? guideText.bounds.width+10 : guideText.bounds.width+30; + + let guideTextRectangle = new paper.Rectangle( + new paper.Point(guideTextRectangleX, targetGroup.path.bounds.y+guideY-35), + new paper.Size(guideTextRectangleWidth, guideText.bounds.height+5) + ); + let guideTextBackground = new paper.Path.Rectangle(guideTextRectangle); + guideTextBackground.fillColor = "rgba(255,255,255,0.75)"; + this.groupTacketGuideLine.addChild(guideLine); + this.groupTacketGuideLine.addChild(guideLineArrow); + this.tacketToolOriginalPosition = this.groupTacketGuideLine.position.x; + this.groupTacketGuide.addChild(this.groupTacketGuideLine); + this.groupTacketGuide.addChild(guideTextBackground); + this.groupTacketGuide.addChild(guideText); + this.groupTacketGuide.addChild(guideLineX1); + this.groupTacketGuide.addChild(guideLineX2); + }, + deactivateTacketTool: function() { + this.tacketToolIsActive = false; + this.groupTacketGuide.removeChildren(); + if (this.tool) { + this.tool.remove(); + } + document.body.style.cursor = "default"; + this.paperGroups.forEach((group)=>group.setMouseEventHandlers()); + this.paperLeaves.forEach((leaf)=>leaf.setMouseEventHandlers()); + }, + drawSewing: function() { + this.paperGroups.forEach((group)=> { + const dirMultiplier = (!group.direction || group.direction === "left-to-right") ? 1 : -1 + if (group.group.sewing!==null && group.group.sewing.length>0) { + const leafID1 = group.group.sewing[0]; + const leafID2 = group.group.sewing.length>1? group.group.sewing[1] : undefined; + + if (leafID1!==undefined && this.leafIDs.indexOf(leafID1)>=0) { + let startX, startY, endX, endY; + let paperLeaf1, paperLeaf2; + + paperLeaf1 = this.getLeaf(this.leafIDs.indexOf(leafID1)+1); + if (leafID2!==undefined) { + paperLeaf2 = this.getLeaf(this.leafIDs.indexOf(leafID2)+1); + startX = paperLeaf1.path.segments[0].point.x-dirMultiplier*this.strokeWidth; + startY = paperLeaf2.path.segments[0].point.y; + endX = paperLeaf2.path.segments[0].point.x; + } else { + startX = 15; + startY = paperLeaf1.path.segments[0].point.y; + endX = paperLeaf1.path.segments[0].point.x; + } + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + startY -= this.spacing*0.15; + } + endY = startY; + let sewingPath = new paper.Path(); + sewingPath.name = "tacket1"; + sewingPath.strokeColor = this.strokeColorTacket; + sewingPath.strokeWidth = 3; + sewingPath.add(new paper.Point(startX, startY)); + sewingPath.add(new paper.Point(endX+dirMultiplier*this.strokeWidth, endY)); + const that = this; + // Add listeners + sewingPath.onClick = function(event) { + that.handleObjectClick(group.group, event); + } + sewingPath.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + sewingPath.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.groupTacket.addChild(sewingPath); + } + } + }); + }, + drawTackets: function() { + this.paperGroups.forEach((group)=> { + const dirMultiplier = (!group.direction || group.direction === "left-to-right") ? 1 : -1 + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + const leafID1 = group.group.tacketed[0]; + const leafID2 =group.group.tacketed[1]; + if (leafID1!==undefined && this.leafIDs.indexOf(leafID1)>=0) { + let startX, startY, endX, endY; + let paperLeaf1, paperLeaf2; + + paperLeaf1 = this.getLeaf(this.leafIDs.indexOf(leafID1)+1); + if (leafID2!==undefined) { + paperLeaf2 = this.getLeaf(this.leafIDs.indexOf(leafID2)+1); + startX = paperLeaf1.path.segments[0].point.x-dirMultiplier*this.strokeWidth; + startY = paperLeaf2.path.segments[0].point.y; + endX = paperLeaf2.path.segments[0].point.x; + } else { + startX = 15; + startY = paperLeaf1.path.segments[0].point.y; + endX = paperLeaf1.path.segments[0].point.x; + } + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + startY -= this.spacing*0.2; + } + if (group.group.sewing!==null && group.group.sewing.length>0) { + startY += this.spacing*0.25; + } + endY = startY; + let tacketPath1 = new paper.Path(); + tacketPath1.name = "tacket1"; + tacketPath1.strokeColor = this.strokeColorTacket; + tacketPath1.strokeWidth = 3; + tacketPath1.add(new paper.Point(startX, startY-2)); + tacketPath1.add(new paper.Point(endX+dirMultiplier*this.strokeWidth, endY-2)); + tacketPath1.add(new paper.Point(tacketPath1.segments[1].point.x+dirMultiplier*5, tacketPath1.segments[1].point.y-3)); + let tacketPath2 = new paper.Path(); + tacketPath2.name = "tacket2"; + tacketPath2.strokeColor = this.strokeColorTacket; + tacketPath2.strokeWidth = 3; + tacketPath2.add(new paper.Point(startX, startY+2)); + tacketPath2.add(new paper.Point(endX+dirMultiplier*this.strokeWidth, endY+2)); + tacketPath2.add(new paper.Point(tacketPath2.segments[1].point.x+dirMultiplier*5, tacketPath2.segments[1].point.y+3)); + const that = this; + // Add listeners + tacketPath1.onClick = function(event) { + that.handleObjectClick(group.group, event); + } + tacketPath2.onClick = function(event) { + that.handleObjectClick(group.group, event); + } + tacketPath1.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + tacketPath1.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + tacketPath2.onMouseEnter = function(event) { + document.body.style.cursor = "pointer"; + } + tacketPath2.onMouseLeave = function(event) { + document.body.style.cursor = "default"; + } + this.groupTacket.addChild(tacketPath1); + this.groupTacket.addChild(tacketPath2); + } + } + }); + }, + getYOfFirstMember: function(groupID) { + let group = this.Groups[groupID]; + if (group.memberIDs.length===0) { + let y = this.groupYs[this.groupIDs.indexOf(group.id)]; + return y; + } + let firstMemberID = group.memberIDs[0]; + let firstMember = this[firstMemberID.split("_")[0]+"s"][firstMemberID]; + if (firstMemberID.memberType==="Group") { + return this.getYOfFirstMember(firstMemberID); + } else { + let firstLeafY = this.leafYs[this.leafIDs.indexOf(firstMember.id)]; + return firstLeafY; + } + }, + getYOfLastMember: function(groupID) { + const group = this.Groups[groupID]; + const lastMember = this.getLastMember(groupID); + if (lastMember && lastMember.memberType==="Group") { + let y = this.groupYs[this.groupIDs.indexOf(lastMember.id)]; + return y+((lastMember.nestLevel-group.nestLevel)*this.spacing); + } else if (lastMember && lastMember.memberType==="Leaf") { + let lastLeafY = this.leafYs[this.leafIDs.indexOf(lastMember.id)] + this.strokeWidth + this.spacing/2.0; + return lastLeafY+((lastMember.nestLevel-group.nestLevel-1)*this.spacing); + } else { + return 0; + } + }, + getLastMember: function(groupID) { + let lastMemberIDs = this.Groups[groupID].memberIDs; + if (lastMemberIDs.length===0) return null; + let lastMemberID = lastMemberIDs[lastMemberIDs.length-1]; + if (lastMemberID.charAt(0)==="L") { + return this.Leafs[lastMemberID]; + } else { + let lastMember = this.Groups[lastMemberID]; + // Check if this group has members + let innerLastMember = this.getLastMember(lastMemberID); + if (innerLastMember) lastMember = innerLastMember; + return lastMember; + } + }, + getGroupHeight: function(group) { + if (group.memberIDs.length>0) { + let height = this.getYOfLastMember(group.id) - this.groupYs[this.groupIDs.indexOf(group.id)]; + return height+this.spacing; + } else { + return this.spacing; + } + }, + numLeaves: function() { + return this.paperLeaves.length; + }, + getLeaf: function(leaf_order) { + return this.paperLeaves[leaf_order-1]; + }, + getLastLeaf: function() { + return this.paperLeaves[this.numLeaves()-1]; + }, + calculateYs: function(members, currentY, spacing) { + if (members.length<1) { + return currentY; + } + let multiplier = 1; + if (members.length>70) { + multiplier = 0.5; + } else if (members.length>45) { + multiplier = 0.6; + } else if (members.length>35 || this.viewingMode) { + multiplier = 0.8; + } + members.forEach((memberID, i)=> { + let memberObject = this[memberID.split("_")[0]+"s"][memberID]; + let notesToShowAbove = memberObject.notes.filter((noteID)=>{return this.Notes[noteID].show&&this.showNotes}).length; + let notesToShowBelow = 0; + let glueSpacing = 0; + if (memberObject.memberType==="Leaf") { + // Find if it has side notes + notesToShowAbove += this.Rectos[memberObject.rectoID].notes.filter((noteID)=>{return this.Notes[noteID].show&&this.showNotes}).length; + notesToShowBelow += this.Versos[memberObject.versoID].notes.filter((noteID)=>{return this.Notes[noteID].show&&this.showNotes}).length; + // Find if leaf has glue that's not a partial glue + glueSpacing = (notesToShowAbove>0 && memberObject.attached_above.includes("Glued") && !memberObject.attached_above.includes("Partial"))? 1 : 0; + } + + if (memberObject.memberType === "Leaf" && getMemberOrder(memberObject, this.Groups, this.groupIDs)===1 && notesToShowAbove>0) { + // First leaf in the group with a note + this.multipliers[this.leafIDs.indexOf(memberObject.id)+1] = multiplier; + currentY = currentY + spacing*(notesToShowAbove+1); + this.leafYs.push(currentY); + currentY = currentY + spacing*notesToShowBelow*0.8; + if (i===(members.length-1)) { + // Last member of group + currentY = currentY + (memberObject.nestLevel)*spacing; + } + } else if (memberObject.memberType==="Leaf" && this.leafIDs.indexOf(memberObject.id)+1 > 0) { + this.multipliers[this.leafIDs.indexOf(memberObject.id)+1] = multiplier; + currentY = currentY + spacing*(Math.max(1,notesToShowAbove)) + spacing*glueSpacing; + if (i > 0 && members[i-1].includes("Group") && this.Groups[members[i-1]].memberIDs.length) { + // Previous sibling is a group with children + // Find difference of nest level between current leaf and previous group's last member + const previousMember = this.getLastMember(members[i-1]); + currentY = currentY + (previousMember.nestLevel - memberObject.nestLevel)*spacing; + } + this.leafYs.push(currentY); + currentY = currentY + spacing*notesToShowBelow*0.8; + } else if (memberObject.memberType==="Group") { + currentY = currentY + spacing; + if (i > 0 && members[i-1].includes("Group") && this.Groups[members[i-1]].memberIDs.length>0) { + // Previous sibling is a group with children + const previousMember = this.getLastMember(members[i-1]); + currentY = currentY + (previousMember.nestLevel - memberObject.nestLevel + 1)*spacing; + } else if (i > 0 && members[i-1].includes("Group")&& this.Groups[members[i-1]].memberIDs.length===0) { + // Previous sibling is a group without children + currentY = currentY + spacing; + } + this.groupYs.push(currentY); + + // Recursify!!! + currentY = this.calculateYs(memberObject.memberIDs, currentY, spacing); + } + }); + return currentY; + }, + fitCanvas: function() { + // Resize canvas so that nothing is cut off + this.canvas.height = this.groupGroups.bounds.bottom+10; + }, + setWidth: function(value) { + this.width = value; + }, + setProject: function(project) { + this.groupIDs = project.groupIDs; + this.leafIDs = project.leafIDs; + this.Groups = project.Groups; + this.Leafs = project.Leafs; + this.Rectos = project.Rectos; + this.Versos = project.Versos; + this.Notes = project.Notes; + }, + setActiveGroups: function(value) { + this.activeGroups = value; + if (this.paperGroups.length>0) { + this.paperGroups.forEach((group)=>{ + group.deactivate(); + }); + this.paperGroups.forEach((paperGroup)=> { + if (this.activeGroups.includes(paperGroup.group.id)) { + paperGroup.activate(); + } + }); + } + }, + setActiveLeafs: function(value) { + this.activeLeafs = value; + if (this.paperLeaves.length>0) { + this.paperLeaves.forEach((leaf)=>{ + leaf.deactivate(); + }); + this.paperLeaves.forEach((paperLeaf)=> { + if (this.activeLeafs.includes(paperLeaf.leaf.id)) { + paperLeaf.activate(); + } + }); + } + }, + setActiveRectos: function(value) { + this.activeRectos = value; + if (this.paperLeaves.length>0) { + this.paperLeaves.forEach((leaf)=>{ + leaf.deactivate(); + }); + this.paperLeaves.forEach((paperLeaf)=> { + if (this.activeRectos.includes(paperLeaf.leaf.rectoID)) { + paperLeaf.activate(); + } + }); + } + }, + setActiveVersos: function(value) { + this.activeVersos = value; + if (this.paperLeaves.length>0) { + this.paperLeaves.forEach((leaf)=>{ + leaf.deactivate(); + }); + this.paperLeaves.forEach((paperLeaf)=> { + if (this.activeVersos.includes(paperLeaf.leaf.versoID)) { + paperLeaf.activate(); + } + }); + } + }, + setFlashItems: function(value) { + this.flashItems = value; + }, + setFilter: function(filters) { + this.filters = filters; + this.showFilter(); + }, + showFilter: function() { + this.paperLeaves.forEach((leaf)=>{ + leaf.filterHighlight.opacity = 0; + if (this.filters.Leafs.includes(leaf.leaf.id) + || this.filters.Sides.includes(leaf.leaf.rectoID) + || this.filters.Sides.includes(leaf.leaf.versoID)) { + leaf.filterHighlight.opacity = 1; + } + }); + this.paperGroups.forEach((group)=>{ + group.filterHighlight.opacity = 0; + if (this.filters.Groups.includes(group.group.id)) { + group.filterHighlight.opacity = 1; + } + }); + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + this.paperGroups.forEach((group)=>group.setVisibility(visibleAttributes.group)); + this.paperLeaves.forEach((leaf)=>leaf.setVisibility(visibleAttributes)); + }, + setScale: function(spacing, strokeWidth) { + this.spacing = this.width*spacing; + this.strokeWidth = this.width*strokeWidth; + }, +} +function PaperManager(args) { + this.canvas = document.getElementById(args.canvasID); + paper.setup(this.canvas); + this.tool = null; + this.allGroupIDs = args.allGroupIDs; + this.allLeafIDs = args.allLeafIDs; + this.groupIDs = args.groupIDs; + this.leafIDs = args.leafIDs; + this.Groups = args.Groups; + this.Leafs = args.Leafs; + this.Rectos = args.Rectos; + this.Versos = args.Versos; + this.Notes = args.Notes; + this.origin = args.origin; + this.width = paper.view.viewSize.width; + this.spacing = this.width*args.spacing; + this.strokeWidth = this.width*args.strokeWidth; + this.strokeColor = args.strokeColor; + this.strokeColorActive = args.strokeColorActive; + this.strokeColorAdded = args.strokeColorAdded; + this.strokeColorGroupActive = args.strokeColorGroupActive; + this.strokeColorTacket = args.strokeColorTacket; + this.groupColor = args.groupColor; + this.groupColorActive = args.groupColorActive; + this.groupTextColor = args.groupTextColor; + this.handleObjectClick = args.handleObjectClick; + this.groupLeaves = new paper.Group();// Groups of leaf paths + this.groupGroups = new paper.Group();// Group of group paths + this.groupContainer = new paper.Group(); + this.activeGroups = args.activeGroups; + this.activeLeafs = args.activeLeafs; + this.activeRectos = args.activeRectos; + this.activeVersos = args.activeVersos; + this.paperLeaves = []; + this.paperGroups = []; + this.leafYs = []; + this.groupYs = []; + this.multipliers = {}; + this.flashItems = args.flashItems; + this.flashLeaves = []; + this.flashGroups = []; + this.filters = args.filters; + this.strokeColorFilter = args.strokeColorFilter; + this.visibleAttributes = args.visibleAttributes; + this.viewingMode = args.viewingMode; + this.tacketLineDrag = new paper.Path(); + this.groupTacketGuide = new paper.Group(); + this.groupTacketGuideLine = new paper.Group(); + this.groupTacket = new paper.Group(); + this.toggleVisualizationDrawing = args.toggleVisualizationDrawing; + this.addVisualization = args.addVisualization; + this.tacketToolIsActive = false; + this.tacketToolOriginalPosition = 0; + this.slideForward = true; + this.openNoteDialog = args.openNoteDialog; + this.leafIDs = args.leafIDs; + this.showNotes = args.showNotes; + + let that = this; + // Flash newly added items + paper.view.onFrame = function(event) { + for (let i=0; i that.tacketToolOriginalPosition+25) { + that.slideForward = false; + } + } else { + that.groupTacketGuideLine.position.x -= 0.5; + if (that.groupTacketGuideLine.position.x < that.tacketToolOriginalPosition) { + that.slideForward = true; + } + } + } + } +} +export default PaperManager; diff --git a/viscoll-app/src/components/authentication/Login.js b/viscoll-app/src/components/authentication/Login.js new file mode 100644 index 00000000..0807d42c --- /dev/null +++ b/viscoll-app/src/components/authentication/Login.js @@ -0,0 +1,100 @@ +import React, { Component } from 'react'; +import ResendConfirmation from './ResendConfirmation'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import { btnLg, btnAuthCancel } from '../../styles/button'; +import {floatFieldDark} from '../../styles/textfield'; + +/** + * Contains the login form that is used by the landing page component called `Landing`. + */ +class Login extends Component { + constructor(props) { + super(props); + this.state = { + email: "", + password: "", + error: "", + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.user.errors.login.errorMessage!==undefined) { + this.setState({error: nextProps.user.errors.login.errorMessage[0]}); + } + } + + componentDidUpdate() { + if (this.props.user.authenticated) { + this.props.history.push("/dashboard"); + } + } + + /** + * Update state when user inputs new value in a text field + */ + onInputChange(v, type) { + this.setState({[type]: v}); + } + /** + * Submit login information and clear any message + */ + submit = (e) => { + if (e) e.preventDefault(); + this.setState({ error: ""}); + this.props.action.loginUser({email: this.state.email, password: this.state.password}); + } + + /** + * Cancel button. Resets local Login state. + */ + cancel = () => { + this.setState({email:"",password:"",error:""}); + this.props.tapCancel(); + } + + render() { + let submitButton = + let content =
+

{this.state.error}

+ this.onInputChange(v,"email")} name="email" floatingLabelText="E-mail" {...floatFieldDark} aria-invalid={this.state.error && this.state.error.length>0} aria-required={true}/> + this.onInputChange(v,"password")} name="password" type="password" floatingLabelText="Password" {...floatFieldDark} aria-invalid={this.state.error && this.state.error.length>0} aria-required={true} /> +

+ {submitButton} +
+ this.cancel()} + label={"Go back"} + {...btnAuthCancel} + /> + this.props.toggleResetRequest()} + /> +
+ + ; + if (this.state.error && this.state.error.includes("unconfirmed")) { + content = + } + return ( + content + ); + } +} + +export default Login; diff --git a/viscoll-app/src/components/authentication/Register.js b/viscoll-app/src/components/authentication/Register.js new file mode 100644 index 00000000..1aab0a82 --- /dev/null +++ b/viscoll-app/src/components/authentication/Register.js @@ -0,0 +1,115 @@ +import React, { Component } from 'react'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import { btnLg, btnAuthCancel } from '../../styles/button'; +import {floatFieldDark} from '../../styles/textfield'; + +/** + * Contains the registration form that is used by the landing page component called `Landing`. + */ +class Register extends Component { + constructor(props) { + super(props); + this.state = { + name: "", + email: "", + password: "", + }; + this.submit = this.submit.bind(this); + } + /** + * Update state when user inputs new value in a text field + */ + onInputChange = (v, type) => { + this.setState({[type]: v}); + } + /** + * Submit registration information + */ + submit = (e) => { + if (e) e.preventDefault(); + this.props.action.registerUser({...this.state}); + } + + render() { + let emailError = ""; + let passwordError = ""; + let registerSuccess = false; + try { + emailError = this.props.userState.errors.register.email; + passwordError = this.props.userState.errors.register.password; + registerSuccess = this.props.userState.registerSuccess; + } catch (e) {} + + let registerForm = ( +
+ this.onInputChange(v, "name")} + name="name" + floatingLabelText="Name" + {...floatFieldDark} + /> + + this.onInputChange(v, "email")} + name="email" + floatingLabelText="E-mail" + {...floatFieldDark} + errorText={emailError} + aria-invalid={emailError && emailError.length>0} + aria-required={true} + /> + + this.onInputChange(v, "password")} + name="password" + floatingLabelText="Password" + {...floatFieldDark} + errorText={passwordError} + type="password" + aria-invalid={passwordError && passwordError.length>0} + aria-required={true} + /> + +

+ +
+ this.props.tapCancel()} + label="Go back" + {...btnAuthCancel} + /> +
+ + ); + + const successMessage = ( +
+

+ Registration successful! You will be notified by email once your account has been approved. +

+
+ this.props.tapCancel()} + label="Okay" + /> +
+ ); + + if (registerSuccess) { + return successMessage; + } else { + return registerForm; + } + } +} +export default Register; diff --git a/viscoll-app/src/components/authentication/ResendConfirmation.js b/viscoll-app/src/components/authentication/ResendConfirmation.js new file mode 100644 index 00000000..68bdef57 --- /dev/null +++ b/viscoll-app/src/components/authentication/ResendConfirmation.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import { btnLg, btnAuthCancel } from '../../styles/button'; +import {floatFieldDark} from '../../styles/textfield'; + +/** + * Resend confirmation form. + */ +class ResendConfirmation extends Component { + constructor(props) { + super(props); + this.state = { + message: props.message, + email: props.email, + submitted: false, + }; + } + + /** + * Update state when user inputs new value in a text field + */ + onChange(v, type) { + this.setState({[type]: v}); + } + + /** + * Send confirmation email + */ + resendConfirmation = () => { + this.props.action.resendConfirmation(this.state.email); + this.setState({submitted:true, message: "An email confirmation has been resent to " + this.state.email}); + } + + render() { + let form = this.state.submitted? "":
+ this.onChange(v,"email")} name="email" floatingLabelText="E-mail" {...floatFieldDark} /> +

+ this.resendConfirmation()} + /> + ; + + let cancel = this.props.tapCancel()} + label="Go back" + {...btnAuthCancel} + />; + + if (this.state.submitted) { + cancel = this.props.tapCancel()} + />; + } + + return ( +
+

{this.state.message}

+ {form} +
+ {cancel} +
+
); + } +} +export default ResendConfirmation; \ No newline at end of file diff --git a/viscoll-app/src/components/authentication/ResetPassword.js b/viscoll-app/src/components/authentication/ResetPassword.js new file mode 100644 index 00000000..50e09f0a --- /dev/null +++ b/viscoll-app/src/components/authentication/ResetPassword.js @@ -0,0 +1,69 @@ +import React, { Component } from 'react'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import { btnLg } from '../../styles/button'; +import {floatFieldDark} from '../../styles/textfield'; +/** + * Contains the form to update password when user forgets password. + */ +class ResetPassword extends Component { + constructor(props) { + super(props); + this.state = { + password: "", + passwordConfirm: "", + resetMessage: "" + }; + } + + /** + * Update state when user inputs new value in a text field + */ + onInputChange = (v, type) => { + this.setState({ [type]: v }); + } + + /** + * Validate password input and submit password change + */ + submit = (e) => { + if (e) e.preventDefault(); + let resetMessage = "" + if (!this.state.password || !this.state.passwordConfirm) { + resetMessage = "Error: Both Password & Password Confirmation must be filled"; + } else if (this.state.password !== this.state.passwordConfirm) { + resetMessage = "Error: Both Password & Password Confirmation must match"; + } + else { + const reset_password_token = this.props.reset_password_token; + const password = { + password: this.state.password, + password_confirmation: this.state.passwordConfirm + }; + this.props.resetPassword(reset_password_token, password); + this.props.handleResetPasswordSuccess(); + } + this.setState({ resetMessage }) + }; + + render() { + return ( +
+

{this.state.resetMessage}

+ this.onInputChange(v, "password")} name="password" type="password" floatingLabelText="New Password" {...floatFieldDark} /> + this.onInputChange(v, "passwordConfirm")} name="passwordConfirm" type="password" floatingLabelText="Confirm New Password" {...floatFieldDark} /> +

+ this.submit(null)} + /> + + ); + } +} +export default ResetPassword; diff --git a/viscoll-app/src/components/authentication/ResetPasswordRequest.js b/viscoll-app/src/components/authentication/ResetPasswordRequest.js new file mode 100644 index 00000000..811dfbb9 --- /dev/null +++ b/viscoll-app/src/components/authentication/ResetPasswordRequest.js @@ -0,0 +1,90 @@ +import React, { Component } from 'react'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import { btnLg, btnAuthCancel } from '../../styles/button'; +import {floatFieldDark} from '../../styles/textfield'; +/** + * Contains the reset password request form that is used by the landing page + * component called `Landing`. User inputs their email address and the app + * will email them a link to change their password. The component that handles the + * password change is `ResetPassword`. + */ +class ResetPasswordRequest extends Component { + constructor(props) { + super(props); + this.state = { + email: "", + resetMessage: "", + requested: false, + }; + } + + /** + * Update state when user inputs new value in a text field + */ + onInputChange(v, type) { + this.setState({[type]: v}); + } + + /** + * Validate email address and submit password reset request + */ + resetPasswordRequest = (e) => { + if (e) e.preventDefault(); + let re = /[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/igm; + let resetMessage = "" + if (!this.state.email) { + resetMessage = "Please enter your email address to reset the password."; + } else if (!re.test(this.state.email)) { + resetMessage = "Please enter a valid email address."; + } + else { + this.props.action.resetPasswordRequest(this.state.email); + resetMessage = "If this email address exists in our system, we've sent a password reset link to it." + this.setState({requested:true}); + } + this.setState({ resetMessage }) + }; + + render() { + let cancelButton = this.props.tapCancel()} + label="Go back" + {...btnAuthCancel} + />; + let submit = +
+ this.resetPasswordRequest(null)} + /> +
; + if (this.state.requested) { + cancelButton = this.props.tapCancel()} + label="Okay" + {...btnLg} + />; + submit = ""; + } + return ( +
+

{this.state.resetMessage}

+ this.onInputChange(v,"email")} name="email" floatingLabelText="E-mail" {...floatFieldDark} /> + { submit } +
+ { cancelButton } +
+ + ) + } +} +export default ResetPasswordRequest; diff --git a/viscoll-app/src/components/collationManager/ExportMode.js b/viscoll-app/src/components/collationManager/ExportMode.js new file mode 100644 index 00000000..d3de2d95 --- /dev/null +++ b/viscoll-app/src/components/collationManager/ExportMode.js @@ -0,0 +1,157 @@ +import React from 'react'; +import PaperManager from "../../assets/visualMode/export/PaperManager.js"; + +/** Contains the collation drawing in a canvas element */ +export default class ExportMode extends React.Component { + constructor(props) { + super(props); + this.state = { + paperManagers: [], + }; + } + + getChildrenByType = (type, memberIDs) => { + let ids = []; + for (let id of memberIDs) { + if (id.includes(type)) ids.push(id); + if (id.includes('Group')) { + let itemMembers = this.props.project.Groups[id].memberIDs; + ids = [...ids, ...this.getChildrenByType(type, itemMembers)]; + } + } + return ids; + } + + componentDidMount() { + this.props.toggleVisualizationDrawing({type:"tacketed", value: ""}); + this.props.toggleVisualizationDrawing({type:"sewing", value: ""}); + window.addEventListener("resize", this.drawOnCanvas); + this.updatePM(); + } + + componentWillUnmount() { + this.props.toggleVisualizationDrawing({type:"tacketed", value: ""}); + this.props.toggleVisualizationDrawing({type:"sewing", value: ""}); + // this.state.paperManager.deactivateTacketTool(); + window.removeEventListener("resize", this.drawOnCanvas); + } + + shouldComponentUpdate(nextProps) { + return (this.props.project.Groups!==nextProps.project.Groups || + this.props.project.Sides!==nextProps.project.Sides || + this.props.project.Rectos!==nextProps.project.Rectos || + this.props.project.Versos!==nextProps.project.Versos || + this.props.project.Notes!==nextProps.project.Notes || + this.props.collationManager.selectedObjects!==nextProps.collationManager.selectedObjects || + this.props.collationManager.flashItems !== nextProps.collationManager.flashItems || + this.props.collationManager.filters !== nextProps.collationManager.filters || + this.props.project.preferences !== nextProps.project.preferences || + this.props.tacketed !== nextProps.tacketed || + this.props.sewing !== nextProps.sewing || + this.props.showNotes !== nextProps.showNotes + ); + } + + componentDidUpdate() { + this.updatePM(); + } + + updatePM = () => { + let pm = []; + for (let [i, [groupID, group]] of Object.entries(this.props.project.Groups).entries()) { + if (group.nestLevel > 1) continue; + // Filter leaf ids for this group only + let memberLeafIDs = this.getChildrenByType('Leaf', group.memberIDs); + let memberGroupIDs = this.getChildrenByType('Group', group.memberIDs); + let memberGroups = {}; + memberGroups[groupID] = this.props.project.Groups[groupID]; + for (let id of memberGroupIDs) { + memberGroups[id] = this.props.project.Groups[id]; + } + pm.push( + new PaperManager({ + canvasID: 'canvas'+i, + origin: 0, + spacing: 0.04, + strokeWidth: 0.015, + strokeColor: 'rgb(82,108,145)', + strokeColorActive: 'rgb(78,214,203)', + strokeColorGroupActive: 'rgb(82,108,145)', + strokeColorFilter: '#95fff6', + strokeColorAdded: "#5F95D6", + groupColor: '#e7e7e7', + groupColorActive: 'rgb(78,214,203)', + groupTextColor: "#727272", + strokeColorTacket: "#4e4e4e", + handleObjectClick: this.props.handleObjectClick, + groupIDs: [this.props.project.groupIDs[i], ...memberGroupIDs], + leafIDs: memberLeafIDs, + allLeafIDs: this.props.project.leafIDs, + allGroupIDs: this.props.project.groupIDs, + Groups: memberGroups, + Leafs: this.props.project.Leafs, + Rectos: this.props.project.Rectos, + Versos: this.props.project.Versos, + Notes: this.props.project.Notes, + activeGroups: this.props.collationManager.selectedObjects.type==="Group"? this.props.collationManager.selectedObjects.members : [], + activeLeafs: this.props.collationManager.selectedObjects.type==="Leaf"? this.props.collationManager.selectedObjects.members : [], + activeRectos: this.props.collationManager.selectedObjects.type==="Recto"? this.props.collationManager.selectedObjects.members : [], + activeVersos: this.props.collationManager.selectedObjects.type==="Verso"? this.props.collationManager.selectedObjects.members : [], + flashItems: this.props.collationManager.flashItems, + filters: this.props.collationManager.filters, + visibleAttributes: this.props.project.preferences, + toggleVisualizationDrawing: this.props.toggleVisualizationDrawing, + addVisualization: this.addVisualization, + openNoteDialog: this.props.openNoteDialog, + showNotes: this.props.showNotes + }) + ) + } + this.setState({paperManagers:pm}, ()=>{this.drawOnCanvas();}); + } + + addVisualization = (groupID, type, leafIDs) => { + let updatedGroup = { + [type]: leafIDs, + } + this.props.updateGroup(groupID, updatedGroup); + } + + /** + * Update canvas size based on current window size + */ + updateCanvasSize = () => { + // Resize the canvas + let maxWidth = window.innerWidth-window.innerWidth*0.46; + document.getElementById("myCanvas").width=maxWidth; + } + + /** + * Draw canvas + */ + drawOnCanvas = () => { + // Create leaves through manager + for (let i = 0; i < this.state.paperManagers.length; i++) { + this.state.paperManagers[i].draw(); + } + } + + render() { + let canvases = []; + let canvasAttr = { + 'data-paper-hidpi': 'off', + 'height': '500', + 'width': window.innerWidth-window.innerWidth*0.46, + }; + for (let [i, [, group]] of Object.entries(this.props.project.Groups).entries()) { + if (group.nestLevel === 1) { + canvases.push() + } + } + return ( +
+ {canvases} +
+ ); + } +} diff --git a/viscoll-app/src/components/collationManager/TabularMode.js b/viscoll-app/src/components/collationManager/TabularMode.js new file mode 100644 index 00000000..32e3cd55 --- /dev/null +++ b/viscoll-app/src/components/collationManager/TabularMode.js @@ -0,0 +1,67 @@ +import React from 'react'; +import update from 'immutability-helper'; +import Group from './tabularMode/Group'; + + +/** Tabular mode - shows collation as table rows */ +export default class TabularMode extends React.Component { + constructor(props) { + super(props); + this.state = { + focusGroupID: [], + focusLeafID: null, + } + } + + toggleFocusGroup = (id) => { + let newList = []; + if (id!==null) { + // Push to array + newList = update(this.state.focusGroupID, {$push: [id]}); + } else { + // Pop the array + newList = update(this.state.focusGroupID, {$splice: [[this.state.focusGroupID.length-1, 1]]}); + } + this.setState({focusGroupID: newList}); + } + + toggleFocusLeaf = (id) => { + this.setState({focusLeafID: id, focusGroupID: [this.state.focusGroupID[this.state.focusGroupID.length-1]]}); + } + + render() { + let group_components = []; + for (let groupID of this.props.project.groupIDs){ + const group = this.props.project.Groups[groupID] + if (group.nestLevel === 1) + group_components.push( + 0? this.state.focusGroupID[this.state.focusGroupID.length-1] : null} + focusLeafID={this.state.focusLeafID} + handleObjectPress={this.props.handleObjectPress} + tabIndex={this.props.tabIndex} + leafIDs={this.props.leafIDs} + /> + ); + } + + let emptyResults = false; + const activeFiltersLength = this.props.collationManager.filters.Groups.length + this.props.collationManager.filters.Leafs.length + this.props.collationManager.filters.Sides.length + this.props.collationManager.filters.Notes.length; + if (activeFiltersLength===0) + emptyResults = true && this.props.collationManager.filters.hideOthers && this.props.collationManager.filters.active + + return ( +
+ {emptyResults ?

No objects match the query

: group_components} +
+ ); + } +} diff --git a/viscoll-app/src/components/collationManager/ViewingMode.js b/viscoll-app/src/components/collationManager/ViewingMode.js new file mode 100644 index 00000000..20d608fd --- /dev/null +++ b/viscoll-app/src/components/collationManager/ViewingMode.js @@ -0,0 +1,145 @@ +import React from 'react'; +import PaperManager from "../../assets/visualMode/PaperManager.js"; +import ImageViewer from "../global/ImageViewer"; + +/** Contains the collation drawing in a canvas element */ +export default class ViewingMode extends React.Component { + constructor(props) { + super(props); + this.state = { + paperManager: {}, + }; + } + + componentDidMount() { + window.addEventListener("resize", this.drawOnCanvas); + this.setState({ + paperManager: new PaperManager({ + canvasID: 'myCanvas', + origin: 0, + spacing: 0.04, + strokeWidth: 0.015, + strokeColor: 'rgb(82,108,145)', + strokeColorActive: 'rgb(78,214,203)', + strokeColorGroupActive: 'rgb(82,108,145)', + strokeColorFilter: '#95fff6', + strokeColorAdded: "#5F95D6", + groupColor: '#e7e7e7', + groupColorActive: 'rgb(78,214,203)', + groupTextColor: "#727272", + strokeColorTacket: "#4e4e4e", + handleObjectClick: this.props.handleObjectClick, + groupIDs: this.props.project.groupIDs, + leafIDs: this.props.project.leafIDs, + Groups: this.props.project.Groups, + Leafs: this.props.project.Leafs, + Rectos: this.props.project.Rectos, + Versos: this.props.project.Versos, + Notes: this.props.project.Notes, + activeGroups: this.props.collationManager.selectedObjects.type==="Group"? this.props.collationManager.selectedObjects.members : [], + activeLeafs: this.props.collationManager.selectedObjects.type==="Leaf"? this.props.collationManager.selectedObjects.members : [], + activeRectos: this.props.collationManager.selectedObjects.type==="Recto"? this.props.collationManager.selectedObjects.members : [], + activeVersos: this.props.collationManager.selectedObjects.type==="Verso"? this.props.collationManager.selectedObjects.members : [], + flashItems: this.props.collationManager.flashItems, + filters: this.props.collationManager.filters, + visibleAttributes: this.props.project.preferences, + toggleTacket: this.props.toggleTacket, + addTacket: this.addTacket, + viewingMode: true, + }) + }, ()=>{this.drawOnCanvas();}); + } + + shouldComponentUpdate(nextProps, nextState) { + return ( + this.props.collationManager.selectedObjects!==nextProps.collationManager.selectedObjects || + this.props.collationManager.filters !== nextProps.collationManager.filters || + this.props.project.preferences !== nextProps.project.preferences || + this.props.project.Notes!==nextProps.project.Notes || + this.state.viewingMode !== nextState.viewingMode || + this.props.imageViewerEnabled !== nextProps.imageViewerEnabled + ); + } + + componentWillUpdate(nextProps, nextState) { + if (Object.keys(this.state.paperManager).length>0) { + this.state.paperManager.setProject(nextProps.project); + this.state.paperManager.setActiveGroups(nextProps.collationManager.selectedObjects.type==="Group"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setActiveLeafs(nextProps.collationManager.selectedObjects.type==="Leaf"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setActiveRectos(nextProps.collationManager.selectedObjects.type==="Recto"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setActiveVersos(nextProps.collationManager.selectedObjects.type==="Verso"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setFilter(nextProps.collationManager.filters); + this.state.paperManager.setVisibility(nextProps.project.preferences); + } + } + + componentDidUpdate() { + this.drawOnCanvas(); + } + + componentWillUnmount() { + window.removeEventListener("resize", this.drawOnCanvas); + } + + /** + * Draw canvas + */ + drawOnCanvas = () => { + // Create leaves through manager + this.updateCanvasSize(); + this.state.paperManager.draw(); + } + /** + * Update canvas size based on current window size + */ + updateCanvasSize = () => { + // Resize the canvas + let maxWidth = window.innerWidth-window.innerWidth*0.46; + if (this.props.imageViewerEnabled) { + maxWidth = window.innerWidth-window.innerWidth*0.75; + } + document.getElementById("myCanvas").width=maxWidth; + this.state.paperManager.setWidth(maxWidth); + if (this.props.imageViewerEnabled) { + this.state.paperManager.setScale(0.06, 0.027); + } else { + this.state.paperManager.setScale(0.04, 0.015); + } + } + + render() { + let canvasAttr = { + 'data-paper-hidpi': 'off', + 'height': "99999999px", + 'width': this.props.imageViewerEnabled? window.innerWidth-window.innerWidth*0.75: window.innerWidth-window.innerWidth*0.46, + }; + + let leafID, leaf, recto, verso, isRectoDIY, isVersoDIY, rectoURL, versoURL; + if (this.props.selectedObjects.type==="Leaf"){ + leafID = this.props.selectedObjects.members[0]; + leaf = this.props.project.Leafs[leafID]; + recto = this.props.project.Rectos[leaf.rectoID]; + verso = this.props.project.Versos[leaf.versoID]; + } else if (this.props.selectedObjects.type==="Recto") { + recto = this.props.project.Rectos[this.props.selectedObjects.members[0]]; + } else if (this.props.selectedObjects.type==="Verso") { + verso = this.props.project.Versos[this.props.selectedObjects.members[0]]; + } + isRectoDIY = recto!==undefined && recto.image.manifestID!==undefined && recto.image.manifestID.includes("DIY"); + isVersoDIY = verso!==undefined && verso.image.manifestID!==undefined && verso.image.manifestID.includes("DIY"); + rectoURL = recto!==undefined && recto.image.url!==undefined? recto.image.url : null; + versoURL = verso!==undefined && verso.image.url!==undefined? verso.image.url : null; + return ( +
+
+ + +
+ {this.props.imageViewerEnabled? + + :"" + } +
+ ); + } +} diff --git a/viscoll-app/src/components/collationManager/VisualMode.js b/viscoll-app/src/components/collationManager/VisualMode.js new file mode 100644 index 00000000..03d58a7d --- /dev/null +++ b/viscoll-app/src/components/collationManager/VisualMode.js @@ -0,0 +1,134 @@ +import React from 'react'; +import PaperManager from "../../assets/visualMode/PaperManager.js"; + +/** Contains the collation drawing in a canvas element */ +export default class VisualMode extends React.Component { + constructor(props) { + super(props); + this.state = { + paperManager: {}, + }; + } + + componentDidMount() { + this.props.toggleVisualizationDrawing({type:"tacketed", value: ""}); + this.props.toggleVisualizationDrawing({type:"sewing", value: ""}); + window.addEventListener("resize", this.drawOnCanvas); + this.setState({ + paperManager: new PaperManager({ + canvasID: 'myCanvas', + origin: 0, + spacing: 0.04, + strokeWidth: 0.015, + strokeColor: 'rgb(82,108,145)', + strokeColorActive: 'rgb(78,214,203)', + strokeColorGroupActive: 'rgb(82,108,145)', + strokeColorFilter: '#95fff6', + strokeColorAdded: "#5F95D6", + groupColor: '#e7e7e7', + groupColorActive: 'rgb(78,214,203)', + groupTextColor: "#727272", + strokeColorTacket: "#4e4e4e", + handleObjectClick: this.props.handleObjectClick, + groupIDs: this.props.project.groupIDs, + leafIDs: this.props.project.leafIDs, + Groups: this.props.project.Groups, + Leafs: this.props.project.Leafs, + Rectos: this.props.project.Rectos, + Versos: this.props.project.Versos, + Notes: this.props.project.Notes, + activeGroups: this.props.collationManager.selectedObjects.type==="Group"? this.props.collationManager.selectedObjects.members : [], + activeLeafs: this.props.collationManager.selectedObjects.type==="Leaf"? this.props.collationManager.selectedObjects.members : [], + activeRectos: this.props.collationManager.selectedObjects.type==="Recto"? this.props.collationManager.selectedObjects.members : [], + activeVersos: this.props.collationManager.selectedObjects.type==="Verso"? this.props.collationManager.selectedObjects.members : [], + flashItems: this.props.collationManager.flashItems, + filters: this.props.collationManager.filters, + visibleAttributes: this.props.project.preferences, + toggleVisualizationDrawing: this.props.toggleVisualizationDrawing, + addVisualization: this.addVisualization, + openNoteDialog: this.props.openNoteDialog, + }) + }, ()=>{this.drawOnCanvas();}); + } + componentWillUnmount() { + this.props.toggleVisualizationDrawing({type:"tacketed", value: ""}); + this.props.toggleVisualizationDrawing({type:"sewing", value: ""}); + this.state.paperManager.deactivateTacketTool(); + window.removeEventListener("resize", this.drawOnCanvas); + } + + shouldComponentUpdate(nextProps) { + return (this.props.project.Groups!==nextProps.project.Groups || + this.props.project.Sides!==nextProps.project.Sides || + this.props.project.Rectos!==nextProps.project.Rectos || + this.props.project.Versos!==nextProps.project.Versos || + this.props.project.Notes!==nextProps.project.Notes || + this.props.collationManager.selectedObjects!==nextProps.collationManager.selectedObjects || + this.props.collationManager.flashItems !== nextProps.collationManager.flashItems || + this.props.collationManager.filters !== nextProps.collationManager.filters || + this.props.project.preferences !== nextProps.project.preferences || + this.props.tacketed !== nextProps.tacketed || + this.props.sewing !== nextProps.sewing + ); + } + + componentWillUpdate(nextProps) { + if (Object.keys(this.state.paperManager).length>0) { + this.state.paperManager.setProject(nextProps.project); + this.state.paperManager.setFlashItems(nextProps.collationManager.flashItems); + this.state.paperManager.setActiveGroups(nextProps.collationManager.selectedObjects.type==="Group"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setActiveLeafs(nextProps.collationManager.selectedObjects.type==="Leaf"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setActiveRectos(nextProps.collationManager.selectedObjects.type==="Recto"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setActiveVersos(nextProps.collationManager.selectedObjects.type==="Verso"? nextProps.collationManager.selectedObjects.members : []); + this.state.paperManager.setFilter(nextProps.collationManager.filters); + this.state.paperManager.setVisibility(nextProps.project.preferences); + this.drawOnCanvas(); + if (nextProps.tacketed!=="") { + this.state.paperManager.activateTacketTool(nextProps.tacketed); + } else if (nextProps.sewing!=="") { + this.state.paperManager.activateTacketTool(nextProps.sewing, "sewing"); + } else { + this.state.paperManager.deactivateTacketTool(); + } + } + } + + addVisualization = (groupID, type, leafIDs) => { + let updatedGroup = { + [type]: leafIDs, + } + this.props.updateGroup(groupID, updatedGroup); + } + + /** + * Update canvas size based on current window size + */ + updateCanvasSize = () => { + // Resize the canvas + let maxWidth = window.innerWidth-window.innerWidth*0.46; + document.getElementById("myCanvas").width=maxWidth; + this.state.paperManager.setWidth(maxWidth); + } + + /** + * Draw canvas + */ + drawOnCanvas = () => { + // Create leaves through manager + this.updateCanvasSize(); + this.state.paperManager.draw(); + } + + render() { + let canvasAttr = { + 'data-paper-hidpi': 'off', + 'height': "99999999px", + 'width': window.innerWidth-window.innerWidth*0.46, + }; + return ( +
+ +
+ ); + } +} diff --git a/viscoll-app/src/components/collationManager/dialog/NoteDialog.js b/viscoll-app/src/components/collationManager/dialog/NoteDialog.js new file mode 100644 index 00000000..b86ecfcb --- /dev/null +++ b/viscoll-app/src/components/collationManager/dialog/NoteDialog.js @@ -0,0 +1,122 @@ +import React from 'react'; +import EditNoteForm from '../../notesManager/EditNoteForm'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; + +/** Note dialog */ +export default class NoteDialog extends React.Component { + getLinkedGroups = () => { + const groupsWithCurrentNote = Object.keys(this.props.Groups).filter((groupID) => { + return (this.props.Groups[groupID].notes.includes(this.props.activeNote.id)) + }); + return groupsWithCurrentNote.map((value) => { + const label = `Group ${this.props.groupIDs.indexOf(value)+1}`; + return {label, value}; + }); + } + + getLinkedLeaves = () => { + const leafsWithCurrentNote = Object.keys(this.props.Leafs).filter((leafID) => { + return (this.props.Leafs[leafID].notes.includes(this.props.activeNote.id)) + }); + return leafsWithCurrentNote.map((value)=>{ + const label = `Leaf ${this.props.leafIDs.indexOf(value)+1}`; + return {label, value}; + }); + } + + getLinkedSides = () => { + const rectosWithCurrentNote = Object.keys(this.props.Rectos).filter((rectoID) => { + return (this.props.Rectos[rectoID].notes.includes(this.props.activeNote.id)) + }); + const versosWithCurrentNote = Object.keys(this.props.Versos).filter((versoID) => { + return (this.props.Versos[versoID].notes.includes(this.props.activeNote.id)) + }); + const sidesWithCurrentNote = []; + for (let value of rectosWithCurrentNote){ + const leafOrder = this.props.leafIDs.indexOf(this.props.Rectos[value].parentID) + 1; + const folioNumber = this.props.Rectos[value].folio_number && this.props.Rectos[value].folio_number!==""? `(${this.props.Rectos[value].folio_number})`:""; + const pageNumber = this.props.Rectos[value].page_number && this.props.Rectos[value].page_number!==""? `(${this.props.Rectos[value].page_number})`:""; + const label = `L${leafOrder} Recto ${folioNumber} ${pageNumber}`; + sidesWithCurrentNote.push({label, value}) + } + for (let value of versosWithCurrentNote){ + const leafOrder = this.props.leafIDs.indexOf(this.props.Versos[value].parentID) + 1; + const folioNumber = this.props.Versos[value].folio_number && this.props.Versos[value].folio_number!==""? `(${this.props.Versos[value].folio_number})`:""; + const pageNumber = this.props.Versos[value].page_number && this.props.Versos[value].page_number!==""? `(${this.props.Versos[value].page_number})`:""; + const label = `L${leafOrder} Verso ${folioNumber} ${pageNumber}`; + sidesWithCurrentNote.push({label, value}) + } + return sidesWithCurrentNote; + } + + getRectosAndVersos = () => { + const size = Object.keys(this.props.Rectos).length; + let result = {}; + for (let i=0; i, + ]; + return ( + + {this.props.open? + : ""} + + ); + } + +} + + + + diff --git a/viscoll-app/src/components/collationManager/tabularMode/Group.js b/viscoll-app/src/components/collationManager/tabularMode/Group.js new file mode 100644 index 00000000..4170039d --- /dev/null +++ b/viscoll-app/src/components/collationManager/tabularMode/Group.js @@ -0,0 +1,154 @@ +import React from 'react'; +import Leaf from './Leaf'; +import IconButton from 'material-ui/IconButton'; +import ExpandMore from 'material-ui/svg-icons/navigation/expand-more'; +import ExpandLess from 'material-ui/svg-icons/navigation/expand-less'; + +/** Group element in the tabular edit mode. Recursively mounts nested groups and leaves. */ +export default class Group extends React.Component { + constructor(props) { + super(props); + this.state = { + open: true, + } + } + + handleChange = (type, value) => { + this.setState({[type]:value}); + } + + findItemByName = (attributeName) => { + return this.props.collationManager.defaultAttributes.group.find((item)=>item.name===attributeName); + } + + render() { + + const isActive = this.props.collationManager.selectedObjects.members.includes(this.props.activeGroup.id); + const isFiltered = this.props.collationManager.filters.Groups.includes(this.props.activeGroup.id); + const groupsOfMatchingElements = this.props.collationManager.filters.GroupsOfMatchingLeafs + this.props.collationManager.filters.GroupsOfMatchingSides + this.props.collationManager.filters.GroupsOfMatchingNotes; + const isAffectedFiltered = groupsOfMatchingElements.includes(this.props.activeGroup.id) && !isFiltered; + const hideOthers = this.props.collationManager.filters.hideOthers; + const isFilterActive = this.props.collationManager.filters.active; + // Populate all the members of this Group. + let groupMembers = []; + this.props.activeGroup.memberIDs.forEach((memberID, index) => { + if (memberID.charAt(0)==="L"){ + let current_leaf = this.props.project.Leafs[memberID]; + groupMembers.push( + + ); + } else { + let current_group = this.props.project.Groups[memberID]; + groupMembers.push( + + ); + } + }); + + let attributes = []; + if (this.props.project.preferences.group) { + for (var attributeName of Object.keys(this.props.project.preferences.group)) { + if (this.props.project.preferences.group[attributeName]) { + const item = this.findItemByName(attributeName); + attributes.push( +
+
+ {item.displayName} + {this.props.activeGroup[attributeName]} +
+
+ ); + } + } + } + + let activeGroupStyle = {}; + if (isFiltered && !hideOthers) { + activeGroupStyle["borderColor"] = "#0f7fdb"; + } + if (isAffectedFiltered && hideOthers && isFilterActive){ + activeGroupStyle["backgroundColor"] = "#d9dbdb"; + activeGroupStyle["borderColor"] = "#d9dbdb"; + } + let groupContainerClasses = "groupContainer "; + if (this.props.collationManager.flashItems.groups.includes(this.props.activeGroup.id)) groupContainerClasses += "flash "; + if (isActive) groupContainerClasses += "active "; + if (this.props.focusLeafID===null && this.props.focusGroupID === this.props.activeGroup.id) groupContainerClasses += "focus "; + + let groupComponent =
this.props.toggleFocusGroup(this.props.activeGroup.id)} + onMouseLeave={()=>this.props.toggleFocusGroup(null)} + onClick={(event) =>this.props.handleObjectClick(this.props.activeGroup, event)} + > +
+
+
+ Group {this.props.activeGroupOrder} + {if(e.key===" "){this.props.handleObjectPress(this.props.activeGroup, e)}}} + onClick={(e)=>{this.props.handleObjectPress(this.props.activeGroup, e);}} + tabIndex={this.props.tabIndex} + /> +
+ {attributes} +
+
+ {e.stopPropagation();e.preventDefault();this.handleChange("open", !this.state.open)}} + aria-label={this.state.open?"Collapse group " + this.props.activeGroupOrder : "Expand group " + this.props.activeGroupOrder } + tabIndex={this.props.tabIndex} + tooltip={this.state.open?"Collapse group" : "Expand group"} + > + {this.state.open? : } + +
+
+
+ {groupMembers} +
+
+ + if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) + groupComponent =
; + + return ( + groupComponent + ); + } + } + + + \ No newline at end of file diff --git a/viscoll-app/src/components/collationManager/tabularMode/Leaf.js b/viscoll-app/src/components/collationManager/tabularMode/Leaf.js new file mode 100644 index 00000000..2d1e5a04 --- /dev/null +++ b/viscoll-app/src/components/collationManager/tabularMode/Leaf.js @@ -0,0 +1,150 @@ +import React from 'react'; +import Side from './Side'; + +/** Leaf element of collation used in tabular edit mode*/ +const Leaf = (props) => { + const { activeLeaf, project } = props; + const { + selectedObjects, + filters, + defaultAttributes, + flashItems, + } = props.collationManager; + const isActive = selectedObjects.members.includes(activeLeaf.id); + const isFiltered = filters.Leafs.includes(activeLeaf.id); + const leafsOfMatchingElements = filters.LeafsOfMatchingSides + filters.LeafsOfMatchingNotes; + const isAffectedFiltered = leafsOfMatchingElements.includes(activeLeaf.id) && !isFiltered; + const hideOthers = filters.hideOthers; + const isFilterActive = filters.active; + let leafAttributes = []; + let sideAttributesActive = false; + + // Determine if any side attributes are active (visibility toggled) + for (let sideAttribute of defaultAttributes.side) { + let attributeName = sideAttribute.name; + if (project.preferences.side && project.preferences.side[attributeName]) { + sideAttributesActive = true; + break; + } + } + + // Render any visible leaf attributes + for (let leafAttribute of defaultAttributes.leaf) { + let attributeName = leafAttribute.name; + if (project.preferences.leaf && project.preferences.leaf[attributeName]) { + let divStyle = "attribute "; + if (isActive) divStyle += "active "; + if (attributeName==="conjoined_to"){ + leafAttributes.push( +
+
+ {leafAttribute.displayName} + {props.leafIDs.indexOf(activeLeaf[attributeName])!==-1 ? `Leaf ${props.leafIDs.indexOf(activeLeaf[attributeName])+1}` : "None"} +
+
+ ); + } else{ + leafAttributes.push( +
+
+ {leafAttribute.displayName} + {activeLeaf[attributeName]} +
+
+ ); + } + } + } + + + + let activeLeafStyle = {}; + if (isFiltered && !hideOthers) { + activeLeafStyle["borderColor"] = "#0f7fdb"; + } + if (isAffectedFiltered && hideOthers && isFilterActive){ + activeLeafStyle["backgroundColor"] = "#d9dbdb"; + activeLeafStyle["borderColor"] = "#d9dbdb"; + } + + let sideComponents; + let sideClassName = "sideToggle"; + if (sideAttributesActive) { + sideClassName = "sideSection"; + } + const rectoSide = props.Rectos[activeLeaf.rectoID]; + const versoSide = props.Versos[activeLeaf.versoID]; + sideComponents = ( +
+ + +
+ ); + + + let sectionStyle = "leafSection "; + if (isActive) sectionStyle += "active "; + if (props.focusLeafID === activeLeaf.id) sectionStyle += "focus"; + + let leafComponent =
+
+
{props.handleObjectClick(activeLeaf, event);event.stopPropagation()}} + style={{ ...activeLeafStyle }} + onMouseEnter={()=>props.toggleFocusLeaf(activeLeaf.id)} + onMouseLeave={()=>props.toggleFocusLeaf(null)} + > +
+ Leaf {props.activeLeafOrder} + {if(e.key===" "){props.handleObjectPress(activeLeaf, e)}}} + onClick={(e)=>{props.handleObjectPress(activeLeaf, e);e.stopPropagation();}} + tabIndex={props.tabIndex} + /> +
+ {leafAttributes.length>0? +
+ {leafAttributes} +
+ :""} +
+ {sideComponents} +
+
+ + if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) + leafComponent =
; + + return ( + leafComponent + ); +} +export default Leaf; diff --git a/viscoll-app/src/components/collationManager/tabularMode/Side.js b/viscoll-app/src/components/collationManager/tabularMode/Side.js new file mode 100644 index 00000000..30541896 --- /dev/null +++ b/viscoll-app/src/components/collationManager/tabularMode/Side.js @@ -0,0 +1,100 @@ +import React from 'react'; + +/** Side element of collation used in tabular edit mode */ +const Side = (props) => { + const { activeSide, project } = props; + const { + selectedObjects, + filters, + defaultAttributes, + } = props.collationManager; + const isActive = selectedObjects.members.includes(activeSide.id); + const sidesOfMatchingElements = filters.SidesOfMatchingNotes; + const isFiltered = filters.Sides.includes(activeSide.id); + const isAffectedFiltered = sidesOfMatchingElements.includes(activeSide.id) && !isFiltered; + const hideOthers = filters.hideOthers; + const isFilterActive = filters.active; + + let sideAttributes = []; + + for (let attribute of defaultAttributes.side) { + if (project.preferences.side && project.preferences.side[attribute.name]) { + sideAttributes.push( +
+ {attribute.displayName}: {activeSide[attribute.name]} +
+ ); + } + } + + let activeSideStyle = {}; + + if (isFiltered && !hideOthers) { + activeSideStyle["borderColor"] = "#0f7fdb"; + } + + if (isAffectedFiltered && hideOthers && isFilterActive){ + activeSideStyle["backgroundColor"] = "#d9dbdb"; + activeSideStyle["borderColor"] = "#d9dbdb"; + } + + const activeSideName = activeSide.id.split("_")[0]; + + let classNames = "side "; + if (props.focusLeafID===props.activeSide.id) classNames += "focus "; + if (isActive) classNames += "active "; + + let sideComponent = ( +
{props.handleObjectClick(activeSide, event); event.stopPropagation();}} + style={{ ...activeSideStyle }} + onMouseEnter={()=>props.toggleFocusLeaf(activeSide.id)} + onMouseLeave={()=>props.toggleFocusLeaf(null)} + > + {activeSideName.charAt(0)} + {if(e.key===" "){props.handleObjectPress(activeSide, e)}}} + onClick={(e)=>{props.handleObjectPress(activeSide, e);e.stopPropagation();}} + tabIndex={props.tabIndex} + /> +
+ ); + + if (sideAttributes.length>0) { + sideComponent = ( +
{props.handleObjectClick(activeSide, event); event.stopPropagation();}} + style={{ ...activeSideStyle }} + onMouseEnter={()=>props.toggleFocusLeaf(activeSide.id)} + onMouseLeave={()=>props.toggleFocusLeaf(null)} + > +
+ {activeSideName} + {if(e.key===" "){props.handleObjectPress(activeSide, e)}}} + onClick={(e)=>{props.handleObjectPress(activeSide, e);e.stopPropagation();}} + tabIndex={props.tabIndex} + /> +
+ {sideAttributes} +
+ ); + } + + return ( + sideComponent + ); +} +export default Side; diff --git a/viscoll-app/src/components/dashboard/CloneProject.js b/viscoll-app/src/components/dashboard/CloneProject.js new file mode 100644 index 00000000..494bfee4 --- /dev/null +++ b/viscoll-app/src/components/dashboard/CloneProject.js @@ -0,0 +1,77 @@ +import React from 'react'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import SelectField from '../global/SelectField'; + +/** New Project dialog - clone existing project */ +export default class CloneProject extends React.Component { + + constructor(props) { + super(props); + this.state = { + projectIndex: -1 + } + } + + onChange = (projectIndex) => { + this.setState({ projectIndex }); + } + + submit = (event) => { + if (event) event.preventDefault(); + this.props.cloneProject(this.props.allProjects[this.state.projectIndex].id); + this.props.reset(); + this.props.close(); + } + + render(){ + if (this.props.allProjects.length>0) { + const data = this.props.allProjects.map((project, index)=>{ + return ( + {text: project.title, value:index} + ); + }); + return ( +
+

Clone Existing Collation

+
+ +
+ this.props.previousStep()} + /> + this.submit(null)} + /> +
+ +
+ ); + } else { + return
+

Clone Existing Collation

+

You do not have any projects to clone.

+
+ this.props.previousStep()} + /> +
+
+ } + } +} diff --git a/viscoll-app/src/components/dashboard/EditProjectForm.js b/viscoll-app/src/components/dashboard/EditProjectForm.js new file mode 100644 index 00000000..68c00cc1 --- /dev/null +++ b/viscoll-app/src/components/dashboard/EditProjectForm.js @@ -0,0 +1,367 @@ +import React from 'react'; +import IconButton from 'material-ui/IconButton'; +import CloseIcon from 'material-ui/svg-icons/navigation/close'; +import RaisedButton from 'material-ui/RaisedButton'; +import TextField from 'material-ui/TextField'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import IconSubmit from 'material-ui/svg-icons/action/done'; +import IconClear from 'material-ui/svg-icons/content/clear'; +import Checkbox from 'material-ui/Checkbox'; + +/** Form to edit project information on the project panel in the dashboard */ +class EditProjectForm extends React.Component { + constructor(props) { + super(props); + this.state = { + unsavedDialog: false, + deleteDialog: false, + deleteUnlinkedImages: false, + editing: { + title: false, + shelfmark: false, + date: false, + }, + errors: { + title: "", + shelfmark: "", + date: "", + }, + }; + } + + /** + * Update project pane if a new project was selected in the dashboard + */ + componentWillReceiveProps(nextProps) { + if (nextProps.selectedProject) { + let title = nextProps.selectedProject.title; + let shelfmark = nextProps.selectedProject.shelfmark; + let date = nextProps.selectedProject.metadata.date; + if (this.props.selectedProject && this.props.selectedProject.id === nextProps.selectedProject.id) { + // Do not update the fields if they are currently editing and did not submit + if (this.state.title !== title && this.state.editing.title) title = this.state.title; + if (this.state.shelfmark !== shelfmark && this.state.editing.shelfmark) shelfmark = this.state.shelfmark; + if (this.state.date !== date && this.state.editing.date) date = this.state.date; + } else { + // Switched project selection - reset editing states + this.setState({ + editing: { + title: false, + shelfmark: false, + date: false, + }, + }); + } + this.setState({ + title: title, + shelfmark: shelfmark, + date: date, + errors: { + title: "", + shelfmark: "", + date: "", + }, + selectedProject: nextProps.selectedProject, + allProjects: nextProps.allProjects, + }) + } + } + + /** + * Validate user input and display appropriate error message + */ + checkValidationError = (type) => { + const errors = {title:"", shelfmark:"", date:""}; + const allProjectsExceptCurrent = [...this.state.allProjects]; + const selectedProjectIndex = this.state.allProjects.findIndex((project)=>project.id===this.props.selectedProject.id); + allProjectsExceptCurrent.splice(selectedProjectIndex, 1); + allProjectsExceptCurrent.forEach(project => { + if (type==="title"){ + if (project.title === this.state.title) + errors.title = "Project title should be unique"; + if (!this.state.title) + errors.title = "Project title is required"; + } else { + if (project.shelfmark === this.state.shelfmark) + errors.shelfmark = "Manuscript shelfmark should be unique"; + if (!this.state.shelfmark) + errors.shelfmark = "Manuscript shelfmark is required"; + } + }); + return errors; + } + + /** + * Return true if any errors exist in the project form + */ + ifErrorsExist = () => { + if (this.state.errors.title) + return true; + if (this.state.errors.shelfmark) + return true; + return false; + } + + /** + * Update state when user inputs new value in a text field + */ + onInputChange = (event, newValue, type) => { + this.setState({[type]: newValue, editing: {...this.state.editing, [type]:true}}, () => { + this.setState({errors: this.checkValidationError(type)}) + }); + } + + /** + * Toggle delete confirmation dialog + */ + handleDeleteDialogToggle = (deleteDialog=false) => { + this.props.togglePopUp(deleteDialog); + this.setState({ deleteDialog }); + }; + + /** + * Toggle unsaved changes dialog + */ + handleUnsavedDialogToggle = (unsavedDialog=false) => { + this.setState({ unsavedDialog }); + }; + + /** + * Submit project update of a specific input field + */ + handleProjectUpdate = (event, field) => { + if (event) event.preventDefault(); + const projectID = this.props.selectedProject.id; + const project = { + title: this.state.title, + shelfmark: this.state.shelfmark, + metadata: { + date: this.state.date + } + }; + const user = { + id: this.props.user.id, + token: this.props.user.token + }; + this.setState({editing: {...this.state.editing, [field]: false }}); + this.props.updateProject(projectID, project, user); + } + /** + * Submit project delete + */ + handleProjectDelete = () => { + this.props.closeProjectPanel(); + this.setState({deleteDialog: false}); + const projectID = this.props.selectedProject.id; + this.props.deleteProject(projectID, this.state.deleteUnlinkedImages); + }; + + /** + * Close project panel + */ + handleProjectPanelClose = (ignoreChanges=false) => { + // Check for any unsaved changes before closing and show the warning dialog. + if (!ignoreChanges && this.isEditing()) + this.setState({ unsavedDialog: true }); + else { + this.setState({ unsavedDialog: false }); + this.props.closeProjectPanel(); + } + } + + /** + * Return true if any input fields have been changed and not saved + */ + isEditing = () => { + return (this.state.editing.title||this.state.editing.shelfmark||this.state.editing.uri||this.state.editing.date); + } + + /** + * Reset text field to original values + */ + handleProjectCancelUpdate = (field) => { + this.setState({ + [field]: this.props.selectedProject[field], + editing: {...this.state.editing, [field]: false }, + errors: {...this.state.errors, [field]: ""}, + }); + } + + /** + * Return a generated HTML of submit and cancel buttons for a specific input name + */ + submitButtons = (field) => { + if (this.state.editing[field]) { + return ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + name="submit" + type="submit" + disabled={this.ifErrorsExist()} + onClick={() => this.handleProjectUpdate(null, field)} + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onClick={() => this.handleProjectCancelUpdate(field)} + /> +
+ ) + } else { + return ""; + } + } + + render() { + const selectedProject = this.props.selectedProject; + if (!selectedProject) + return
; + + let projectPanelData = ( +
+
this.handleProjectUpdate(e, "title")}> + 0} + onChange={(event, newValue) => this.onInputChange(event, newValue, "title")} + floatingLabelStyle={{fontSize: 25, color: "#526C91"}} + fullWidth={true} + tabIndex={this.props.tabIndex} + autoFocus={true} + /> + {this.submitButtons("title")} + +
this.handleProjectUpdate(e, "shelfmark")}> + this.onInputChange(event, newValue, "shelfmark")} + floatingLabelStyle={{fontSize: 25, color: "#526C91"}} + fullWidth={true} + tabIndex={this.props.tabIndex} + /> + {this.submitButtons("shelfmark")} + +
this.handleProjectUpdate(e, "date")}> + 0} + onChange={(event, newValue) => this.onInputChange(event, newValue, "date")} + floatingLabelStyle={{fontSize: 25, color: "#526C91"}} + fullWidth={true} + hintText="N/A" + tabIndex={this.props.tabIndex} + /> + {this.submitButtons("date")} + +
+ Created at: {new Date(selectedProject.created_at).toLocaleString('en-US')}
+ Last modified: {new Date(selectedProject.updated_at).toLocaleString('en-US')}
+
+ +
+ ); + + const deleteActions = [ + this.handleDeleteDialogToggle()} + />, + this.handleProjectDelete()} + />, + ]; + + const unsaveActions = [ + this.handleUnsavedDialogToggle()} + />, + this.handleProjectPanelClose(true)} + />, + ]; + + + return ( +
+ + this.handleProjectPanelClose()} + tabIndex={this.props.tabIndex} + > + + + + {projectPanelData} + +
+ + this.props.history.push(`/project/${this.props.selectedProject.id}`)} + style={{width:"49%",float:"left",marginRight:"2%"}} + tabIndex={this.props.tabIndex} + /> + this.handleDeleteDialogToggle(true)} + labelColor="#b53c3c" + style={{width:"49%"}} + tabIndex={this.props.tabIndex} + /> + + + this.setState({deleteUnlinkedImages: !this.state.deleteUnlinkedImages})} + /> + + + + +
+ ); + } +} +export default EditProjectForm; diff --git a/viscoll-app/src/components/dashboard/ImageCollections.js b/viscoll-app/src/components/dashboard/ImageCollections.js new file mode 100644 index 00000000..ee2455bd --- /dev/null +++ b/viscoll-app/src/components/dashboard/ImageCollections.js @@ -0,0 +1,306 @@ +import React, {Component} from 'react'; +import { Grid } from 'react-virtualized'; +import Checkbox from 'material-ui/Checkbox'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import ChipInput from 'material-ui-chip-input' +import Popover from 'material-ui/Popover'; +import Menu from 'material-ui/Menu'; +import MenuItem from 'material-ui/MenuItem'; +import IconFilter from 'material-ui/svg-icons/content/filter-list'; +import ArrowDropRight from 'material-ui/svg-icons/navigation-arrow-drop-right'; +import RemoveImageConfirmation from '../imageManager/RemoveImageConfirmation'; +import UploadImages from '../imageManager/UploadImages'; +import { btnBase } from '../../styles/button'; + +/** Image collection page in dashboard section */ +class ImageCollection extends Component { + constructor(props) { + super(props); + this.state = { + columnCount: 3, + selectedImages: props.images? props.images.map((image)=>false):[], + filterOpen: false, + filter: {value:"all", text:"Show all images"}, + removeConfirmationOpen: "", + windowWidth: window.innerWidth, + gridWidth: window.innerWidth*0.50, + gridHeight: window.innerHeight-150, + columnWidth: window.innerWidth*0.50*0.33, + }; + } + + componentWillReceiveProps(nextProps) { + if (this.state.selectedImages.length===0||nextProps.images.length!==this.props.images.length) { + this.setState({selectedImages:nextProps.images.map((image)=>false)}); + } + } + + componentDidMount() { + window.addEventListener("resize", this.windowResize); + } + + componentWillUnmount() { + window.removeEventListener("resize", this.windowResize); + } + + toggleConfirmation = (value) => { + this.setState({removeConfirmationOpen:value}); + if (value.length>0) { + this.props.togglePopUp(true); + } else { + this.props.togglePopUp(false); + } + } + + windowResize = () => { + this.setState({ + windowWidth: window.innerWidth, + gridWidth: window.innerWidth*0.50, + gridHeight: window.innerHeight-150, + columnWidth: window.innerWidth*0.50*0.33, + }); + } + + handleFilterClick = (e) => { + e.preventDefault(); // Prevent ghost click + this.setState({ + filterOpen: true, + anchorEl: e.currentTarget, + }); + } + handleFilterClose = () => { + this.setState({ + filterOpen: false, + }); + } + handleFilterChoice = (value, text) => { + this.setState({ + filter: {value, text}, + filterOpen: false, + selectedImages: this.props.images.map((image)=>false), + }) + } + + toggleCheckbox = (index) => { + let newArray = Object.assign([], this.state.selectedImages); + newArray[index] = !newArray[index]; + this.setState({selectedImages: newArray}, ()=>this.forceUpdate()); + } + + cellRenderer = ({ columnIndex, key, rowIndex, style }, imagesToRender) => { + const index = this.state.columnCount*rowIndex+columnIndex; + if (indeximage.id===img.id); + return ( +
+
+ {img.label} +
+ {this.toggleCheckbox(globalIndex)}} + labelStyle={{overflow:"hidden", textOverflow: "ellipsis", wordWrap:"break-word", width:this.state.windowWidth*0.50*0.25-50}} + /> +
+ ) + } + } + + getActiveImages = () => { + let ids=[]; + for (let i=0; i { + return this.props.projects.find((project)=>project.id===projectID); + } + + /** + * Returns items in common + */ + intersect = (list1, list2) => { + if (list1.length >= list2.length) + return list1.filter((id1)=>{return list2.includes(id1)}); + else + return list2.filter((id1)=>{return list1.includes(id1)}); + } + + handleAddChip = (chip) => { + // Link project to selected images + this.props.action.linkImages([chip.id],this.getActiveImages().map((img)=>img.id)); + } + + handleDeleteChip = (chip, index) => { + // Unlink project from selected images + this.props.action.unlinkImages([chip],this.getActiveImages().map((img)=>img.id)); + } + + selectAll = () => { + let selectedImages = []; + if (this.state.filter.value === "all") { + selectedImages = this.props.images.map(()=>true); + } else if (this.state.filter.value === "orphans") { + selectedImages = this.props.images.map((img)=>img.projectIDs.length===0); + } else { + // Filter is a project ID + selectedImages = this.props.images.map((image)=>{if (image.projectIDs.includes(this.state.filter.value)) { return true } else { return false }}); + } + this.setState({selectedImages}); + } + + render() { + if (this.props.images) { + let imagesToRender = this.props.images; + if (this.state.filter.value.includes("orphans")) { + imagesToRender = imagesToRender.filter((img)=>img.projectIDs.length===0); + } else if (this.state.filter.value!=="all") { + imagesToRender = imagesToRender.filter((img)=>img.projectIDs.includes(this.state.filter.value)); + } + + // Generate info panel + let infoPanel =

Select one or more images to edit

+ const numSelected = this.state.selectedImages.filter((x)=>x).length; + const activeImages = this.getActiveImages(); + if (numSelected>0) { + let projectInfo = ""; + // More than one image selected + // Find all the projects in common + let projectDataSource = this.props.projects.map((project)=>{return {id:project.id,title:project.title}}); + let commonProjectIDs = activeImages[0].projectIDs; + let commonProjectDataSource = []; + for (let img of activeImages) { + commonProjectIDs = this.intersect(commonProjectIDs, img.projectIDs); + } + commonProjectIDs.forEach((id)=>commonProjectDataSource.push({id:id, title:this.getProject(id).title})); + projectInfo =
+

{numSelected>1?"Projects in common":"Projects"}

+ this.handleAddChip(chip)} + onRequestDelete={(chip, index) => this.handleDeleteChip(chip, index)} + dataSource={projectDataSource} + dataSourceConfig={{text:'title', value:'id'}} + openOnFocus={true} + fullWidth + hintText={"Choose project.."} + /> +
+ infoPanel =
+

{numSelected} image{numSelected>1?"s":""} selected

+ {projectInfo} + {this.toggleConfirmation("delete")}} + backgroundColor="#b53c3c" + labelColor="#ffffff" + fullWidth + /> +
+ } + // Generate file upload panel + const uploadPanel =
+

Upload images

+ +
+ // Generate filter panel + const filterPanel =
+
+ } + {...btnBase()} + /> + + + this.handleFilterChoice("all", "Show all images")} primaryText="Show all images" /> + this.handleFilterChoice("orphans", "Show orphaned images")} primaryText="Show orphaned images" /> + } + menuItems={this.props.projects.map((project)=>this.handleFilterChoice(project.id, project.title)} />)} + /> + + +
+
+ !x)===-1} + /> + this.setState({selectedImages:this.props.images.map((image)=>false)})} + labelStyle={this.state.windowWidth<=768?{fontSize:"0.6em"}:{}} + disabled={this.state.selectedImages.findIndex((x)=>x)===-1} + /> +
+
+ + return
+
+
+ {filterPanel} + this.cellRenderer(data, imagesToRender)} + columnCount={this.state.columnCount} + columnWidth={this.state.columnWidth} + height={this.state.gridHeight} + rowCount={imagesToRender.length%3===0? imagesToRender.length/3 : Math.floor(imagesToRender.length/3)+1} + rowHeight={200} + width={this.state.gridWidth} + id="grid" + /> +
+
+ {numSelected>0?infoPanel:uploadPanel} +
+
+ 0} + toggleConfirmation={this.toggleConfirmation} + deleteImages={this.props.action.deleteImages} + unlinkImages={this.props.action.unlinkImages} + imgs={activeImages.map((img)=>{return {id:img.id, label:img.label}})} + actionType={"delete"} + numToRemove={numSelected} + collectionsMode={true} + /> +
+ } else { + return
+ } + } +} +export default ImageCollection; \ No newline at end of file diff --git a/viscoll-app/src/components/dashboard/ImportProject.js b/viscoll-app/src/components/dashboard/ImportProject.js new file mode 100644 index 00000000..f16b0559 --- /dev/null +++ b/viscoll-app/src/components/dashboard/ImportProject.js @@ -0,0 +1,163 @@ +import React from 'react'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; + +/** New Project dialog - import existing project */ +export default class ImportProject extends React.Component { + + constructor(props) { + super(props); + this.state = { + importData: "", + importFormat: "json", + imageData:"", + } + } + + isDisabled = () => { + if (this.state.importData) + return (this.state.importData.length===0); + else + return true + } + + submit = (event) => { + if (event) event.preventDefault(); + if (!this.isDisabled()) { + this.props.importProject({importData: this.state.importData, importFormat: this.state.importFormat, imageData: this.state.imageData}); + } + } + + onChange = (value, type) => { + this.setState({ [type]: value }); + } + + componentWillReceiveProps = (nextProps) => { + if (nextProps.importStatus==="SUCCESS") { + nextProps.reset(); + nextProps.close(); + } + } + + checkIfFileTypeIsInvalid = (file) => { + const allowedFileTypes = ["json", "xml"]; + return !allowedFileTypes.includes(file.type) + } + + + handleFileSelected = (files) => { + let file = files[0]; + let importFormat = file.type.split("/")[1]; + let reader = new FileReader(); + reader.readAsText(file); + reader.onloadend = ()=>this.setState({importData: reader.result, importFormat}) + } + + handleImageFile = (files) => { + let file = files[0]; + let reader = new FileReader(); + reader.readAsDataURL(file); + reader.onloadend = ()=> {this.setState({imageData: reader.result})} + } + + render() { + let xmlMessage = ""; + if (this.state.importFormat==="xml") + xmlMessage =

Note: If the XML file was not originally created by this application, + some attributes and mappings may not be successfully imported. + However, the collation structure will always be importable from any XML file that follows the VisColl schema.

+ return ( +
+
+

Import

+
+

Import collation

+

Upload your exported collation file or directly paste the content of the file in the textbox below.

+
+
+ this.handleFileSelected(event.target.files)} + onClick={(event)=>event.target.value=null} + /> +
+
+